Skip to content

Commit

Permalink
[201911]show interface counters for multi ASIC devices (#1104)
Browse files Browse the repository at this point in the history
Signed-off-by: Arvindsrinivasan Lakshmi Narasimhan <[email protected]>
Add support for multi ASIC CLI options for show interface portchannels. This PR is port of #1013 to 201911
- Change the portstat script to get the interface counters from all the Namespaces
- Add support for -n and -s options to portstat scripts
- Add unit test for single and multi asic
  • Loading branch information
arlakshm authored Sep 10, 2020
1 parent ff80b67 commit cd8b421
Show file tree
Hide file tree
Showing 7 changed files with 1,417 additions and 41 deletions.
98 changes: 73 additions & 25 deletions scripts/portstat
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,31 @@ import getopt
import os.path
import re
import subprocess
import swsssdk
import sys
import time

from collections import namedtuple, OrderedDict
from natsort import natsorted
from tabulate import tabulate
from sonic_py_common import multi_asic
from utilities_common import constants
import utilities_common.multi_asic as multi_asic_util
from utilities_common.netstat import ns_diff, ns_brate, ns_prate, ns_util, table_as_json

# mock the redis for unit test purposes #
try:
if os.environ["UTILITIES_UNIT_TESTING"] == "2":
modules_path = os.path.join(os.path.dirname(__file__), "..")
tests_path = os.path.join(modules_path, "sonic-utilities-tests")
sys.path.insert(0, modules_path)
sys.path.insert(0, tests_path)
import mock_tables.dbconnector
if os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] == "multi_asic" :
import mock_tables.mock_multi_asic
mock_tables.dbconnector.load_namespace_config()
except KeyError:
pass

PORT_RATE = 40

NStats = namedtuple("NStats", "rx_ok, rx_err, rx_drop, rx_ovr, tx_ok,\
Expand Down Expand Up @@ -63,11 +79,25 @@ PORT_STATE_UP = 'U'
PORT_STATE_DOWN = 'D'
PORT_STATE_DISABLED = 'X'


class Portstat(object):
def __init__(self):
self.db = swsssdk.SonicV2Connector(host='127.0.0.1')
self.db.connect(self.db.COUNTERS_DB)
self.db.connect(self.db.APPL_DB)
def __init__(self, namespace, display_option):
self.db = None
self.multi_asic = multi_asic_util.MultiAsic(display_option, namespace)

def get_cnstat_dict(self):
self.cnstat_dict = OrderedDict()
self.cnstat_dict['time'] = datetime.datetime.now()
self.collect_stat()
return self.cnstat_dict

@multi_asic_util.run_on_multi_asic
def collect_stat(self):
"""
Collect the statisitics from all the asics present on the
device and store in a dict
"""
self.cnstat_dict.update(self.get_cnstat())

def get_cnstat(self):
"""
Expand Down Expand Up @@ -96,6 +126,9 @@ class Portstat(object):
if counter_port_name_map is None:
return cnstat_dict
for port in natsorted(counter_port_name_map):
port_name = port.split(":")[0]
if self.multi_asic.skip_display(constants.PORT_OBJ, port_name):
continue
cnstat_dict[port] = get_counters(counter_port_name_map[port])
return cnstat_dict

Expand All @@ -105,30 +138,35 @@ class Portstat(object):
"""
# Get speed from APPL_DB
full_table_id = PORT_STATUS_TABLE_PREFIX + port_name
speed = self.db.get(self.db.APPL_DB, full_table_id, PORT_SPEED_FIELD)
if speed is None:
speed = PORT_RATE
else:
speed = int(speed)/1000
speed = PORT_RATE
for ns in self.multi_asic.get_ns_list_based_on_options():
self.db = multi_asic.connect_to_all_dbs_for_ns(ns)
speed = self.db.get(self.db.APPL_DB, full_table_id, PORT_SPEED_FIELD)
if speed is not None:
return int(speed)//1000
return speed

def get_port_state(self, port_name):
"""
Get the port state
"""
full_table_id = PORT_STATUS_TABLE_PREFIX + port_name
admin_state = self.db.get(self.db.APPL_DB, full_table_id, PORT_ADMIN_STATUS_FIELD)
oper_state = self.db.get(self.db.APPL_DB, full_table_id, PORT_OPER_STATUS_FIELD)
if admin_state is None or oper_state is None:
return STATUS_NA
elif admin_state.upper() == PORT_STATUS_VALUE_DOWN:
return PORT_STATE_DISABLED
elif admin_state.upper() == PORT_STATUS_VALUE_UP and oper_state.upper() == PORT_STATUS_VALUE_UP:
return PORT_STATE_UP
elif admin_state.upper() == PORT_STATUS_VALUE_UP and oper_state.upper() == PORT_STATUS_VALUE_DOWN:
return PORT_STATE_DOWN
else:
return STATUS_NA
for ns in self.multi_asic.get_ns_list_based_on_options():
self.db = multi_asic.connect_to_all_dbs_for_ns(ns)
admin_state = self.db.get(self.db.APPL_DB, full_table_id, PORT_ADMIN_STATUS_FIELD)
oper_state = self.db.get(self.db.APPL_DB, full_table_id, PORT_OPER_STATUS_FIELD)

if admin_state is None or oper_state is None:
continue
if admin_state.upper() == PORT_STATUS_VALUE_DOWN:
return PORT_STATE_DISABLED
elif admin_state.upper() == PORT_STATUS_VALUE_UP and oper_state.upper() == PORT_STATUS_VALUE_UP:
return PORT_STATE_UP
elif admin_state.upper() == PORT_STATUS_VALUE_UP and oper_state.upper() == PORT_STATUS_VALUE_DOWN:
return PORT_STATE_DOWN
else:
return STATUS_NA
return STATUS_NA

def cnstat_print(self, cnstat_dict, use_json, print_all):
"""
Expand Down Expand Up @@ -272,6 +310,8 @@ Examples:
parser.add_argument('-a', '--all', action='store_true', help='Display all the stats counters')
parser.add_argument('-t', '--tag', type=str, help='Save stats with name TAG', default=None)
parser.add_argument('-p', '--period', type=int, help='Display stats over a specified period (in seconds).', default=0)
parser.add_argument('-s','--show', default=constants.DISPLAY_EXTERNAL, help='Display all interfaces or only external interfaces')
parser.add_argument('-n','--namespace', default=None, help='Display interfaces for specific namespace')
args = parser.parse_args()

save_fresh_stats = args.clear
Expand All @@ -283,6 +323,8 @@ Examples:
uid = str(os.getuid())
wait_time_in_seconds = args.period
print_all = args.all
namespace = args.namespace
display_option = args.show

if tag_name is not None:
cnstat_file = uid + "-" + tag_name
Expand Down Expand Up @@ -315,8 +357,14 @@ Examples:
os.rmdir(cnstat_dir)
sys.exit(0)

portstat = Portstat()
cnstat_dict = portstat.get_cnstat()
# When saving counters to the file, save counters
# for all ports(Internal and External)
if save_fresh_stats:
namespace = None
display_option = constants.DISPLAY_ALL

portstat = Portstat(namespace, display_option)
cnstat_dict = portstat.get_cnstat_dict()

# Now decide what information to display
if raw_stats:
Expand Down Expand Up @@ -360,7 +408,7 @@ Examples:
#wait for the specified time and then gather the new stats and output the difference.
time.sleep(wait_time_in_seconds)
print "The rates are calculated within %s seconds period" % wait_time_in_seconds
cnstat_new_dict = portstat.get_cnstat()
cnstat_new_dict = portstat.get_cnstat_dict()
portstat.cnstat_diff_print(cnstat_new_dict, cnstat_dict, use_json, print_all)

if __name__ == "__main__":
Expand Down
7 changes: 6 additions & 1 deletion show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -835,9 +835,10 @@ def status(interfacename, namespace, display, verbose):
@interfaces.group(invoke_without_command=True)
@click.option('-a', '--printall', is_flag=True)
@click.option('-p', '--period')
@multi_asic_util.multi_asic_click_options
@click.option('--verbose', is_flag=True, help="Enable verbose output")
@click.pass_context
def counters(ctx, verbose, period, printall):
def counters(ctx, verbose, period, printall, namespace, display):
"""Show interface counters"""

if ctx.invoked_subcommand is None:
Expand All @@ -848,6 +849,10 @@ def counters(ctx, verbose, period, printall):
if period is not None:
cmd += " -p {}".format(period)

cmd += " -s {}".format(display)
if namespace is not None:
cmd += " -n {}".format(namespace)

run_command(cmd, display_cmd=verbose)

# 'counters' subcommand ("show interfaces counters rif")
Expand Down
24 changes: 24 additions & 0 deletions sonic-utilities-tests/mock_tables/asic0/counters_db.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@
"Packets": "1001"
},
"COUNTERS:oid:0x1000000000002": {
"SAI_PORT_STAT_IF_IN_UCAST_PKTS": "8",
"SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS": "0",
"SAI_PORT_STAT_IF_IN_OCTETS" : "800",
"SAI_PORT_STAT_IF_OUT_UCAST_PKTS": "10",
"SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS" : "0",
"SAI_PORT_STAT_IF_OUT_OCTETS" : "1000",
"SAI_PORT_STAT_IF_IN_ERRORS": "10",
"SAI_PORT_STAT_IF_IN_DISCARDS": "100",
"SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "80",
Expand All @@ -142,6 +148,12 @@
"SAI_PORT_STAT_PFC_7_TX_PKTS": "207"
},
"COUNTERS:oid:0x1000000000004": {
"SAI_PORT_STAT_IF_IN_UCAST_PKTS": "4",
"SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS": "0",
"SAI_PORT_STAT_IF_IN_OCTETS" : "400",
"SAI_PORT_STAT_IF_OUT_UCAST_PKTS": "40",
"SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS" : "0",
"SAI_PORT_STAT_IF_OUT_OCTETS" : "4000",
"SAI_PORT_STAT_IF_IN_ERRORS": "0",
"SAI_PORT_STAT_IF_IN_DISCARDS": "1000",
"SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "800",
Expand All @@ -164,6 +176,12 @@
"SAI_PORT_STAT_PFC_7_TX_PKTS": "407"
},
"COUNTERS:oid:0x1000000000006": {
"SAI_PORT_STAT_IF_IN_UCAST_PKTS": "6",
"SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS": "0",
"SAI_PORT_STAT_IF_IN_OCTETS" : "600",
"SAI_PORT_STAT_IF_OUT_UCAST_PKTS": "60",
"SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS" : "0",
"SAI_PORT_STAT_IF_OUT_OCTETS" : "6000",
"SAI_PORT_STAT_IF_IN_ERRORS": "0",
"SAI_PORT_STAT_IF_IN_DISCARDS": "1000",
"SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "800",
Expand All @@ -186,6 +204,12 @@
"SAI_PORT_STAT_PFC_7_TX_PKTS": "607"
},
"COUNTERS:oid:0x1000000000008": {
"SAI_PORT_STAT_IF_IN_UCAST_PKTS": "8",
"SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS": "0",
"SAI_PORT_STAT_IF_IN_OCTETS" : "800",
"SAI_PORT_STAT_IF_OUT_UCAST_PKTS": "80",
"SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS" : "0",
"SAI_PORT_STAT_IF_OUT_OCTETS" : "8000",
"SAI_PORT_STAT_IF_IN_ERRORS": "0",
"SAI_PORT_STAT_IF_IN_DISCARDS": "1000",
"SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "800",
Expand Down
Loading

0 comments on commit cd8b421

Please sign in to comment.