Skip to content

Commit d5f5382

Browse files
authored
[CLI][queue counters] add JSON output option for queue counters (sonic-net#1505)
+ added tests for 'show queue counters' CLI
1 parent 176cc4a commit d5f5382

File tree

5 files changed

+1508
-29
lines changed

5 files changed

+1508
-29
lines changed

scripts/queuestat

+103-22
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,33 @@
22

33
#####################################################################
44
#
5-
# queuestat is a tool for summarizing queue statistics of all ports.
5+
# queuestat is a tool for summarizing queue statistics of all ports.
66
#
77
#####################################################################
88

99
import _pickle as pickle
1010
import argparse
1111
import datetime
1212
import os.path
13-
from swsscommon.swsscommon import SonicV2Connector
1413
import sys
1514

1615
from collections import namedtuple, OrderedDict
1716
from natsort import natsorted
1817
from tabulate import tabulate
1918

19+
# mock the redis for unit test purposes #
20+
try:
21+
if os.environ["UTILITIES_UNIT_TESTING"] == "2":
22+
modules_path = os.path.join(os.path.dirname(__file__), "..")
23+
tests_path = os.path.join(modules_path, "tests")
24+
sys.path.insert(0, modules_path)
25+
sys.path.insert(0, tests_path)
26+
import mock_tables.dbconnector # lgtm [py/unused-import]
27+
28+
except KeyError:
29+
pass
30+
31+
from swsscommon.swsscommon import SonicV2Connector
2032

2133
QueueStats = namedtuple("QueueStats", "queueindex, queuetype, totalpacket, totalbytes, droppacket, dropbytes")
2234
header = ['Port', 'TxQ', 'Counter/pkts', 'Counter/bytes', 'Drop/pkts', 'Drop/bytes']
@@ -28,6 +40,7 @@ counter_bucket_dict = {
2840
'SAI_QUEUE_STAT_DROPPED_BYTES': 5,
2941
}
3042

43+
from utilities_common.cli import json_dump
3144
from utilities_common.netstat import ns_diff, STATUS_NA
3245

3346
QUEUE_TYPE_MC = 'MC'
@@ -47,6 +60,24 @@ COUNTERS_QUEUE_PORT_MAP = "COUNTERS_QUEUE_PORT_MAP"
4760
cnstat_dir = 'N/A'
4861
cnstat_fqn_file = 'N/A'
4962

63+
64+
def build_json(port, cnstat):
65+
def ports_stats(k):
66+
p = {}
67+
p[k[1]] = {
68+
"totalpacket": k[2],
69+
"totalbytes": k[3],
70+
"droppacket": k[4],
71+
"dropbytes": k[5]
72+
}
73+
return p
74+
75+
out = {}
76+
for k in cnstat:
77+
out.update(ports_stats(k))
78+
return out
79+
80+
5081
class Queuestat(object):
5182
def __init__(self):
5283
self.db = SonicV2Connector(use_unix_socket_path=False)
@@ -134,33 +165,45 @@ class Queuestat(object):
134165
if queue_map is None:
135166
return cnstat_dict
136167
for queue in natsorted(queue_map):
137-
cnstat_dict[queue] = get_counters(queue_map[queue])
168+
cnstat_dict[queue] = get_counters(queue_map[queue])
138169
return cnstat_dict
139170

140-
def cnstat_print(self, port, cnstat_dict):
171+
def cnstat_print(self, port, cnstat_dict, json_opt):
141172
"""
142-
Print the cnstat.
173+
Print the cnstat. If JSON option is True, return data in
174+
JSON format.
143175
"""
144176
table = []
177+
json_output = {port: {}}
145178

146179
for key, data in cnstat_dict.items():
147180
if key == 'time':
181+
if json_opt:
182+
json_output[port][key] = data
148183
continue
149184
table.append((port, data.queuetype + str(data.queueindex),
150185
data.totalpacket, data.totalbytes,
151186
data.droppacket, data.dropbytes))
152187

153-
print(tabulate(table, header, tablefmt='simple', stralign='right'))
154-
print()
188+
if json_opt:
189+
json_output[port].update(build_json(port, table))
190+
return json_output
191+
else:
192+
print(tabulate(table, header, tablefmt='simple', stralign='right'))
193+
print()
155194

156-
def cnstat_diff_print(self, port, cnstat_new_dict, cnstat_old_dict):
195+
def cnstat_diff_print(self, port, cnstat_new_dict, cnstat_old_dict, json_opt):
157196
"""
158-
Print the difference between two cnstat results.
197+
Print the difference between two cnstat results. If JSON
198+
option is True, return data in JSON format.
159199
"""
160200
table = []
201+
json_output = {port: {}}
161202

162203
for key, cntr in cnstat_new_dict.items():
163204
if key == 'time':
205+
if json_opt:
206+
json_output[port][key] = cntr
164207
continue
165208
old_cntr = None
166209
if key in cnstat_old_dict:
@@ -177,42 +220,78 @@ class Queuestat(object):
177220
cntr.totalpacket, cntr.totalbytes,
178221
cntr.droppacket, cntr.dropbytes))
179222

180-
print(tabulate(table, header, tablefmt='simple', stralign='right'))
181-
print()
223+
if json_opt:
224+
json_output[port].update(build_json(port, table))
225+
return json_output
226+
else:
227+
print(tabulate(table, header, tablefmt='simple', stralign='right'))
228+
print()
182229

183-
def get_print_all_stat(self):
184-
# Get stat for each port
230+
def get_print_all_stat(self, json_opt):
231+
"""
232+
Get stat for each port
233+
If JSON option is True, collect data for each port and
234+
print data in JSON format for all ports
235+
"""
236+
json_output = {}
185237
for port in natsorted(self.counter_port_name_map):
238+
json_output[port] = {}
186239
cnstat_dict = self.get_cnstat(self.port_queues_map[port])
187240

188241
cnstat_fqn_file_name = cnstat_fqn_file + port
189242
if os.path.isfile(cnstat_fqn_file_name):
190243
try:
191244
cnstat_cached_dict = pickle.load(open(cnstat_fqn_file_name, 'rb'))
192-
print(port + " Last cached time was " + str(cnstat_cached_dict.get('time')))
193-
self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict)
245+
if json_opt:
246+
json_output[port].update({"cached_time":cnstat_cached_dict.get('time')})
247+
json_output.update(self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt))
248+
else:
249+
print(port + " Last cached time was " + str(cnstat_cached_dict.get('time')))
250+
self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt)
194251
except IOError as e:
195252
print(e.errno, e)
196253
else:
197-
self.cnstat_print(port, cnstat_dict)
254+
if json_opt:
255+
json_output.update(self.cnstat_print(port, cnstat_dict, json_opt))
256+
else:
257+
self.cnstat_print(port, cnstat_dict, json_opt)
198258

199-
def get_print_port_stat(self, port):
259+
if json_opt:
260+
print(json_dump(json_output))
261+
262+
def get_print_port_stat(self, port, json_opt):
263+
"""
264+
Get stat for the port
265+
If JSON option is True print data in JSON format
266+
"""
200267
if not port in self.port_queues_map:
201268
print("Port doesn't exist!", port)
202269
sys.exit(1)
203270

204271
# Get stat for the port queried
205272
cnstat_dict = self.get_cnstat(self.port_queues_map[port])
206273
cnstat_fqn_file_name = cnstat_fqn_file + port
274+
json_output = {}
275+
json_output[port] = {}
207276
if os.path.isfile(cnstat_fqn_file_name):
208277
try:
209278
cnstat_cached_dict = pickle.load(open(cnstat_fqn_file_name, 'rb'))
210-
print("Last cached time was " + str(cnstat_cached_dict.get('time')))
211-
self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict)
279+
if json_opt:
280+
json_output[port].update({"cached_time":cnstat_cached_dict.get('time')})
281+
json_output.update(self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt))
282+
else:
283+
print("Last cached time was " + str(cnstat_cached_dict.get('time')))
284+
self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt)
212285
except IOError as e:
213286
print(e.errno, e)
214287
else:
215-
self.cnstat_print(port, cnstat_dict)
288+
if json_opt:
289+
json_output.update(self.cnstat_print(port, cnstat_dict, json_opt))
290+
else:
291+
self.cnstat_print(port, cnstat_dict, json_opt)
292+
293+
if json_opt:
294+
print(json_dump(json_output))
216295

217296
def save_fresh_stats(self):
218297
if not os.path.exists(cnstat_dir):
@@ -251,10 +330,12 @@ Examples:
251330
parser.add_argument('-c', '--clear', action='store_true', help='Clear previous stats and save new ones')
252331
parser.add_argument('-d', '--delete', action='store_true', help='Delete saved stats')
253332
parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0')
333+
parser.add_argument('-j', '--json_opt', action='store_true', help='Print in JSON format')
254334
args = parser.parse_args()
255335

256336
save_fresh_stats = args.clear
257337
delete_all_stats = args.delete
338+
json_opt = args.json_opt
258339

259340
port_to_show_stats = args.port
260341

@@ -282,9 +363,9 @@ Examples:
282363
sys.exit(0)
283364

284365
if port_to_show_stats!=None:
285-
queuestat.get_print_port_stat(port_to_show_stats)
366+
queuestat.get_print_port_stat(port_to_show_stats, json_opt)
286367
else:
287-
queuestat.get_print_all_stat()
368+
queuestat.get_print_all_stat(json_opt)
288369

289370
sys.exit(0)
290371

show/main.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,8 @@ def queue():
520520
@queue.command()
521521
@click.argument('interfacename', required=False)
522522
@click.option('--verbose', is_flag=True, help="Enable verbose output")
523-
def counters(interfacename, verbose):
523+
@click.option('--json', is_flag=True, help="JSON output")
524+
def counters(interfacename, verbose, json):
524525
"""Show queue counters"""
525526

526527
cmd = "queuestat"
@@ -532,6 +533,9 @@ def counters(interfacename, verbose):
532533
if interfacename is not None:
533534
cmd += " -p {}".format(interfacename)
534535

536+
if json:
537+
cmd += " -j"
538+
535539
run_command(cmd, display_cmd=verbose)
536540

537541
#

0 commit comments

Comments
 (0)