Skip to content

Commit

Permalink
[CLI][queue counters] add JSON output option for queue counters (soni…
Browse files Browse the repository at this point in the history
…c-net#1505)

+ added tests for 'show queue counters' CLI
  • Loading branch information
smaheshm committed Apr 16, 2021
1 parent 176cc4a commit d5f5382
Show file tree
Hide file tree
Showing 5 changed files with 1,508 additions and 29 deletions.
125 changes: 103 additions & 22 deletions scripts/queuestat
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,33 @@

#####################################################################
#
# queuestat is a tool for summarizing queue statistics of all ports.
# queuestat is a tool for summarizing queue statistics of all ports.
#
#####################################################################

import _pickle as pickle
import argparse
import datetime
import os.path
from swsscommon.swsscommon import SonicV2Connector
import sys

from collections import namedtuple, OrderedDict
from natsort import natsorted
from tabulate import tabulate

# 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, "tests")
sys.path.insert(0, modules_path)
sys.path.insert(0, tests_path)
import mock_tables.dbconnector # lgtm [py/unused-import]

except KeyError:
pass

from swsscommon.swsscommon import SonicV2Connector

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

from utilities_common.cli import json_dump
from utilities_common.netstat import ns_diff, STATUS_NA

QUEUE_TYPE_MC = 'MC'
Expand All @@ -47,6 +60,24 @@ COUNTERS_QUEUE_PORT_MAP = "COUNTERS_QUEUE_PORT_MAP"
cnstat_dir = 'N/A'
cnstat_fqn_file = 'N/A'


def build_json(port, cnstat):
def ports_stats(k):
p = {}
p[k[1]] = {
"totalpacket": k[2],
"totalbytes": k[3],
"droppacket": k[4],
"dropbytes": k[5]
}
return p

out = {}
for k in cnstat:
out.update(ports_stats(k))
return out


class Queuestat(object):
def __init__(self):
self.db = SonicV2Connector(use_unix_socket_path=False)
Expand Down Expand Up @@ -134,33 +165,45 @@ class Queuestat(object):
if queue_map is None:
return cnstat_dict
for queue in natsorted(queue_map):
cnstat_dict[queue] = get_counters(queue_map[queue])
cnstat_dict[queue] = get_counters(queue_map[queue])
return cnstat_dict

def cnstat_print(self, port, cnstat_dict):
def cnstat_print(self, port, cnstat_dict, json_opt):
"""
Print the cnstat.
Print the cnstat. If JSON option is True, return data in
JSON format.
"""
table = []
json_output = {port: {}}

for key, data in cnstat_dict.items():
if key == 'time':
if json_opt:
json_output[port][key] = data
continue
table.append((port, data.queuetype + str(data.queueindex),
data.totalpacket, data.totalbytes,
data.droppacket, data.dropbytes))

print(tabulate(table, header, tablefmt='simple', stralign='right'))
print()
if json_opt:
json_output[port].update(build_json(port, table))
return json_output
else:
print(tabulate(table, header, tablefmt='simple', stralign='right'))
print()

def cnstat_diff_print(self, port, cnstat_new_dict, cnstat_old_dict):
def cnstat_diff_print(self, port, cnstat_new_dict, cnstat_old_dict, json_opt):
"""
Print the difference between two cnstat results.
Print the difference between two cnstat results. If JSON
option is True, return data in JSON format.
"""
table = []
json_output = {port: {}}

for key, cntr in cnstat_new_dict.items():
if key == 'time':
if json_opt:
json_output[port][key] = cntr
continue
old_cntr = None
if key in cnstat_old_dict:
Expand All @@ -177,42 +220,78 @@ class Queuestat(object):
cntr.totalpacket, cntr.totalbytes,
cntr.droppacket, cntr.dropbytes))

print(tabulate(table, header, tablefmt='simple', stralign='right'))
print()
if json_opt:
json_output[port].update(build_json(port, table))
return json_output
else:
print(tabulate(table, header, tablefmt='simple', stralign='right'))
print()

def get_print_all_stat(self):
# Get stat for each port
def get_print_all_stat(self, json_opt):
"""
Get stat for each port
If JSON option is True, collect data for each port and
print data in JSON format for all ports
"""
json_output = {}
for port in natsorted(self.counter_port_name_map):
json_output[port] = {}
cnstat_dict = self.get_cnstat(self.port_queues_map[port])

cnstat_fqn_file_name = cnstat_fqn_file + port
if os.path.isfile(cnstat_fqn_file_name):
try:
cnstat_cached_dict = pickle.load(open(cnstat_fqn_file_name, 'rb'))
print(port + " Last cached time was " + str(cnstat_cached_dict.get('time')))
self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict)
if json_opt:
json_output[port].update({"cached_time":cnstat_cached_dict.get('time')})
json_output.update(self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt))
else:
print(port + " Last cached time was " + str(cnstat_cached_dict.get('time')))
self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt)
except IOError as e:
print(e.errno, e)
else:
self.cnstat_print(port, cnstat_dict)
if json_opt:
json_output.update(self.cnstat_print(port, cnstat_dict, json_opt))
else:
self.cnstat_print(port, cnstat_dict, json_opt)

def get_print_port_stat(self, port):
if json_opt:
print(json_dump(json_output))

def get_print_port_stat(self, port, json_opt):
"""
Get stat for the port
If JSON option is True print data in JSON format
"""
if not port in self.port_queues_map:
print("Port doesn't exist!", port)
sys.exit(1)

# Get stat for the port queried
cnstat_dict = self.get_cnstat(self.port_queues_map[port])
cnstat_fqn_file_name = cnstat_fqn_file + port
json_output = {}
json_output[port] = {}
if os.path.isfile(cnstat_fqn_file_name):
try:
cnstat_cached_dict = pickle.load(open(cnstat_fqn_file_name, 'rb'))
print("Last cached time was " + str(cnstat_cached_dict.get('time')))
self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict)
if json_opt:
json_output[port].update({"cached_time":cnstat_cached_dict.get('time')})
json_output.update(self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt))
else:
print("Last cached time was " + str(cnstat_cached_dict.get('time')))
self.cnstat_diff_print(port, cnstat_dict, cnstat_cached_dict, json_opt)
except IOError as e:
print(e.errno, e)
else:
self.cnstat_print(port, cnstat_dict)
if json_opt:
json_output.update(self.cnstat_print(port, cnstat_dict, json_opt))
else:
self.cnstat_print(port, cnstat_dict, json_opt)

if json_opt:
print(json_dump(json_output))

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

save_fresh_stats = args.clear
delete_all_stats = args.delete
json_opt = args.json_opt

port_to_show_stats = args.port

Expand Down Expand Up @@ -282,9 +363,9 @@ Examples:
sys.exit(0)

if port_to_show_stats!=None:
queuestat.get_print_port_stat(port_to_show_stats)
queuestat.get_print_port_stat(port_to_show_stats, json_opt)
else:
queuestat.get_print_all_stat()
queuestat.get_print_all_stat(json_opt)

sys.exit(0)

Expand Down
6 changes: 5 additions & 1 deletion show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,8 @@ def queue():
@queue.command()
@click.argument('interfacename', required=False)
@click.option('--verbose', is_flag=True, help="Enable verbose output")
def counters(interfacename, verbose):
@click.option('--json', is_flag=True, help="JSON output")
def counters(interfacename, verbose, json):
"""Show queue counters"""

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

if json:
cmd += " -j"

run_command(cmd, display_cmd=verbose)

#
Expand Down
Loading

0 comments on commit d5f5382

Please sign in to comment.