Skip to content

Commit 8d802d4

Browse files
authored
show interface counters for multi ASIC devices (sonic-net#1013)
Signed-off-by: Arvindsrinivasan Lakshmi Narasimhan <[email protected]> Add support for multi ASIC CLI options for show interface counters - 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 tests for single and multi asic
1 parent 96cb359 commit 8d802d4

File tree

8 files changed

+758
-49
lines changed

8 files changed

+758
-49
lines changed

scripts/portstat

100644100755
+83-30
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,35 @@ import argparse
1010
import cPickle as pickle
1111
import datetime
1212
import os.path
13-
import swsssdk
1413
import sys
1514
import time
15+
from collections import OrderedDict, namedtuple
16+
1617

17-
from collections import namedtuple, OrderedDict
1818
from natsort import natsorted
1919
from tabulate import tabulate
20-
from utilities_common.netstat import ns_diff, ns_brate, ns_prate, ns_util, table_as_json
20+
from sonic_py_common import multi_asic
21+
22+
from utilities_common import constants
2123
from utilities_common.intf_filter import parse_interface_in_filter
24+
import utilities_common.multi_asic as multi_asic_util
25+
from utilities_common.netstat import (ns_brate, ns_diff, ns_prate, ns_util,
26+
table_as_json)
27+
28+
# mock the redis for unit test purposes #
29+
try:
30+
if os.environ["UTILITIES_UNIT_TESTING"] == "2":
31+
modules_path = os.path.join(os.path.dirname(__file__), "..")
32+
tests_path = os.path.join(modules_path, "tests")
33+
sys.path.insert(0, modules_path)
34+
sys.path.insert(0, tests_path)
35+
import mock_tables.dbconnector
36+
if os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] == "multi_asic":
37+
import mock_tables.mock_multi_asic
38+
mock_tables.dbconnector.load_namespace_config()
39+
40+
except KeyError:
41+
pass
2242

2343
PORT_RATE = 40
2444

@@ -62,11 +82,25 @@ PORT_STATE_UP = 'U'
6282
PORT_STATE_DOWN = 'D'
6383
PORT_STATE_DISABLED = 'X'
6484

85+
6586
class Portstat(object):
66-
def __init__(self):
67-
self.db = swsssdk.SonicV2Connector(host='127.0.0.1')
68-
self.db.connect(self.db.COUNTERS_DB)
69-
self.db.connect(self.db.APPL_DB)
87+
def __init__(self, namespace, display_option):
88+
self.db = None
89+
self.multi_asic = multi_asic_util.MultiAsic(display_option, namespace)
90+
91+
def get_cnstat_dict(self):
92+
self.cnstat_dict = OrderedDict()
93+
self.cnstat_dict['time'] = datetime.datetime.now()
94+
self.collect_stat()
95+
return self.cnstat_dict
96+
97+
@multi_asic_util.run_on_multi_asic
98+
def collect_stat(self):
99+
"""
100+
Collect the statisitics from all the asics present on the
101+
device and store in a dict
102+
"""
103+
self.cnstat_dict.update(self.get_cnstat())
70104

71105
def get_cnstat(self):
72106
"""
@@ -91,10 +125,12 @@ class Portstat(object):
91125
counter_port_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_PORT_NAME_MAP);
92126
# Build a dictionary of the stats
93127
cnstat_dict = OrderedDict()
94-
cnstat_dict['time'] = datetime.datetime.now()
95128
if counter_port_name_map is None:
96129
return cnstat_dict
97130
for port in natsorted(counter_port_name_map):
131+
port_name = port.split(":")[0]
132+
if self.multi_asic.skip_display(constants.PORT_OBJ, port_name):
133+
continue
98134
cnstat_dict[port] = get_counters(counter_port_name_map[port])
99135
return cnstat_dict
100136

@@ -104,30 +140,36 @@ class Portstat(object):
104140
"""
105141
# Get speed from APPL_DB
106142
full_table_id = PORT_STATUS_TABLE_PREFIX + port_name
107-
speed = self.db.get(self.db.APPL_DB, full_table_id, PORT_SPEED_FIELD)
108-
if speed is None:
109-
speed = PORT_RATE
110-
else:
111-
speed = int(speed)//1000
143+
speed = PORT_RATE
144+
for ns in self.multi_asic.get_ns_list_based_on_options():
145+
self.db = multi_asic.connect_to_all_dbs_for_ns(ns)
146+
speed = self.db.get(self.db.APPL_DB, full_table_id, PORT_SPEED_FIELD)
147+
if speed is not None:
148+
return int(speed)//1000
112149
return speed
113150

114151
def get_port_state(self, port_name):
115152
"""
116153
Get the port state
117154
"""
118155
full_table_id = PORT_STATUS_TABLE_PREFIX + port_name
119-
admin_state = self.db.get(self.db.APPL_DB, full_table_id, PORT_ADMIN_STATUS_FIELD)
120-
oper_state = self.db.get(self.db.APPL_DB, full_table_id, PORT_OPER_STATUS_FIELD)
121-
if admin_state is None or oper_state is None:
122-
return STATUS_NA
123-
elif admin_state.upper() == PORT_STATUS_VALUE_DOWN:
124-
return PORT_STATE_DISABLED
125-
elif admin_state.upper() == PORT_STATUS_VALUE_UP and oper_state.upper() == PORT_STATUS_VALUE_UP:
126-
return PORT_STATE_UP
127-
elif admin_state.upper() == PORT_STATUS_VALUE_UP and oper_state.upper() == PORT_STATUS_VALUE_DOWN:
128-
return PORT_STATE_DOWN
129-
else:
130-
return STATUS_NA
156+
for ns in self.multi_asic.get_ns_list_based_on_options():
157+
self.db = multi_asic.connect_to_all_dbs_for_ns(ns)
158+
admin_state = self.db.get(self.db.APPL_DB, full_table_id, PORT_ADMIN_STATUS_FIELD)
159+
oper_state = self.db.get(self.db.APPL_DB, full_table_id, PORT_OPER_STATUS_FIELD)
160+
161+
if admin_state is None or oper_state is None:
162+
continue
163+
if admin_state.upper() == PORT_STATUS_VALUE_DOWN:
164+
return PORT_STATE_DISABLED
165+
elif admin_state.upper() == PORT_STATUS_VALUE_UP and oper_state.upper() == PORT_STATUS_VALUE_UP:
166+
return PORT_STATE_UP
167+
elif admin_state.upper() == PORT_STATUS_VALUE_UP and oper_state.upper() == PORT_STATUS_VALUE_DOWN:
168+
return PORT_STATE_DOWN
169+
else:
170+
return STATUS_NA
171+
return STATUS_NA
172+
131173

132174
def cnstat_print(self, cnstat_dict, intf_list, use_json, print_all, errors_only, rates_only):
133175
"""
@@ -171,6 +213,7 @@ class Portstat(object):
171213
else:
172214
print(tabulate(table, header, tablefmt='simple', stralign='right'))
173215

216+
174217
def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict, intf_list, use_json, print_all, errors_only, rates_only):
175218
"""
176219
Print the difference between two cnstat results.
@@ -311,6 +354,8 @@ Examples:
311354
parser.add_argument('-t', '--tag', type=str, help='Save stats with name TAG', default=None)
312355
parser.add_argument('-p', '--period', type=int, help='Display stats over a specified period (in seconds).', default=0)
313356
parser.add_argument('-i', '--interface', type=str, help='Display stats for interface lists.', default=None)
357+
parser.add_argument('-s','--show', default=constants.DISPLAY_EXTERNAL, help='Display all interfaces or only external interfaces')
358+
parser.add_argument('-n','--namespace', default=None, help='Display interfaces for specific namespace')
314359
args = parser.parse_args()
315360

316361
save_fresh_stats = args.clear
@@ -325,6 +370,9 @@ Examples:
325370
wait_time_in_seconds = args.period
326371
print_all = args.all
327372
intf_fs = args.interface
373+
namespace = args.namespace
374+
display_option = args.show
375+
328376
if tag_name is not None:
329377
cnstat_file = uid + "-" + tag_name
330378
else:
@@ -358,9 +406,14 @@ Examples:
358406

359407
intf_list = parse_interface_in_filter(intf_fs)
360408

361-
portstat = Portstat()
362-
# The cnstat_dict just give an ordered dict of all output.
363-
cnstat_dict = portstat.get_cnstat()
409+
# When saving counters to the file, save counters
410+
# for all ports(Internal and External)
411+
if save_fresh_stats:
412+
namespace = None
413+
display_option = constants.DISPLAY_ALL
414+
415+
portstat = Portstat(namespace, display_option)
416+
cnstat_dict = portstat.get_cnstat_dict()
364417

365418
# Now decide what information to display
366419
if raw_stats:
@@ -403,8 +456,8 @@ Examples:
403456
else:
404457
#wait for the specified time and then gather the new stats and output the difference.
405458
time.sleep(wait_time_in_seconds)
406-
print("The rates are calculated within %s seconds period" % wait_time_in_seconds)
407-
cnstat_new_dict = portstat.get_cnstat()
459+
print "The rates are calculated within %s seconds period" % wait_time_in_seconds
460+
cnstat_new_dict = portstat.get_cnstat_dict()
408461
portstat.cnstat_diff_print(cnstat_new_dict, cnstat_dict, intf_list, use_json, print_all, errors_only, rates_only)
409462

410463
if __name__ == "__main__":

show/interfaces/__init__.py

+18-3
Original file line numberDiff line numberDiff line change
@@ -355,9 +355,10 @@ def presence(db, interfacename, namespace, verbose):
355355
@click.option('-a', '--printall', is_flag=True)
356356
@click.option('-p', '--period')
357357
@click.option('-i', '--interface')
358+
@multi_asic_util.multi_asic_click_options
358359
@click.option('--verbose', is_flag=True, help="Enable verbose output")
359360
@click.pass_context
360-
def counters(ctx, verbose, period, interface, printall):
361+
def counters(ctx, verbose, period, interface, printall, namespace, display):
361362
"""Show interface counters"""
362363

363364
if ctx.invoked_subcommand is None:
@@ -369,29 +370,43 @@ def counters(ctx, verbose, period, interface, printall):
369370
cmd += " -p {}".format(period)
370371
if interface is not None:
371372
cmd += " -i {}".format(interface)
373+
else:
374+
cmd += " -s {}".format(display)
375+
if namespace is not None:
376+
cmd += " -n {}".format(namespace)
372377

373378
clicommon.run_command(cmd, display_cmd=verbose)
374379

375380
# 'errors' subcommand ("show interfaces counters errors")
376381
@counters.command()
377382
@click.option('-p', '--period')
383+
@multi_asic_util.multi_asic_click_options
378384
@click.option('--verbose', is_flag=True, help="Enable verbose output")
379-
def errors(verbose, period):
385+
def errors(verbose, period, namespace, display):
380386
"""Show interface counters errors"""
381387
cmd = "portstat -e"
382388
if period is not None:
383389
cmd += " -p {}".format(period)
390+
391+
cmd += " -s {}".format(display)
392+
if namespace is not None:
393+
cmd += " -n {}".format(namespace)
394+
384395
clicommon.run_command(cmd, display_cmd=verbose)
385396

386397
# 'rates' subcommand ("show interfaces counters rates")
387398
@counters.command()
388399
@click.option('-p', '--period')
400+
@multi_asic_util.multi_asic_click_options
389401
@click.option('--verbose', is_flag=True, help="Enable verbose output")
390-
def rates(verbose, period):
402+
def rates(verbose, period, namespace, display):
391403
"""Show interface counters rates"""
392404
cmd = "portstat -R"
393405
if period is not None:
394406
cmd += " -p {}".format(period)
407+
cmd += " -s {}".format(display)
408+
if namespace is not None:
409+
cmd += " -n {}".format(namespace)
395410
clicommon.run_command(cmd, display_cmd=verbose)
396411

397412
# 'counters' subcommand ("show interfaces counters rif")

show/main.py

+7-12
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,26 @@
11
#! /usr/bin/python -u
22

33
import json
4-
import netaddr
5-
import netifaces
64
import os
7-
import re
85
import subprocess
96
import sys
107

11-
128
import click
139
from natsort import natsorted
10+
import netifaces
1411
from pkg_resources import parse_version
15-
from sonic_py_common import device_info
16-
from swsssdk import ConfigDBConnector
17-
from swsssdk import SonicV2Connector
18-
from tabulate import tabulate
19-
20-
import utilities_common.cli as clicommon
21-
from utilities_common.db import Db
22-
from utilities_common import multi_asic as multi_asic_util
2312

2413
import feature
2514
import interfaces
2615
import kube
2716
import mlnx
17+
import utilities_common.cli as clicommon
2818
import vlan
19+
from sonic_py_common import device_info
20+
from swsssdk import ConfigDBConnector, SonicV2Connector
21+
from tabulate import tabulate
22+
from utilities_common.db import Db
23+
import utilities_common.multi_asic as multi_asic_util
2924

3025
# Global Variables
3126
PLATFORM_JSON = 'platform.json'

tests/mock_tables/asic0/counters_db.json

+24
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,12 @@
14641464
"Packets": "1001"
14651465
},
14661466
"COUNTERS:oid:0x1000000000002": {
1467+
"SAI_PORT_STAT_IF_IN_UCAST_PKTS": "8",
1468+
"SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS": "0",
1469+
"SAI_PORT_STAT_IF_IN_OCTETS" : "800",
1470+
"SAI_PORT_STAT_IF_OUT_UCAST_PKTS": "10",
1471+
"SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS" : "0",
1472+
"SAI_PORT_STAT_IF_OUT_OCTETS" : "1000",
14671473
"SAI_PORT_STAT_IF_IN_ERRORS": "10",
14681474
"SAI_PORT_STAT_IF_IN_DISCARDS": "100",
14691475
"SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "80",
@@ -1486,6 +1492,12 @@
14861492
"SAI_PORT_STAT_PFC_7_TX_PKTS": "207"
14871493
},
14881494
"COUNTERS:oid:0x1000000000004": {
1495+
"SAI_PORT_STAT_IF_IN_UCAST_PKTS": "4",
1496+
"SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS": "0",
1497+
"SAI_PORT_STAT_IF_IN_OCTETS" : "400",
1498+
"SAI_PORT_STAT_IF_OUT_UCAST_PKTS": "40",
1499+
"SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS" : "0",
1500+
"SAI_PORT_STAT_IF_OUT_OCTETS" : "4000",
14891501
"SAI_PORT_STAT_IF_IN_ERRORS": "0",
14901502
"SAI_PORT_STAT_IF_IN_DISCARDS": "1000",
14911503
"SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "800",
@@ -1508,6 +1520,12 @@
15081520
"SAI_PORT_STAT_PFC_7_TX_PKTS": "407"
15091521
},
15101522
"COUNTERS:oid:0x1000000000006": {
1523+
"SAI_PORT_STAT_IF_IN_UCAST_PKTS": "6",
1524+
"SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS": "0",
1525+
"SAI_PORT_STAT_IF_IN_OCTETS" : "600",
1526+
"SAI_PORT_STAT_IF_OUT_UCAST_PKTS": "60",
1527+
"SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS" : "0",
1528+
"SAI_PORT_STAT_IF_OUT_OCTETS" : "6000",
15111529
"SAI_PORT_STAT_IF_IN_ERRORS": "0",
15121530
"SAI_PORT_STAT_IF_IN_DISCARDS": "1000",
15131531
"SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "800",
@@ -1530,6 +1548,12 @@
15301548
"SAI_PORT_STAT_PFC_7_TX_PKTS": "607"
15311549
},
15321550
"COUNTERS:oid:0x1000000000008": {
1551+
"SAI_PORT_STAT_IF_IN_UCAST_PKTS": "8",
1552+
"SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS": "0",
1553+
"SAI_PORT_STAT_IF_IN_OCTETS" : "800",
1554+
"SAI_PORT_STAT_IF_OUT_UCAST_PKTS": "80",
1555+
"SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS" : "0",
1556+
"SAI_PORT_STAT_IF_OUT_OCTETS" : "8000",
15331557
"SAI_PORT_STAT_IF_IN_ERRORS": "0",
15341558
"SAI_PORT_STAT_IF_IN_DISCARDS": "1000",
15351559
"SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE": "800",

0 commit comments

Comments
 (0)