Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
MuhammadUmarAsad authored Jun 16, 2023
2 parents 3faca88 + 3ba8241 commit 7177585
Show file tree
Hide file tree
Showing 20 changed files with 822 additions and 96 deletions.
12 changes: 12 additions & 0 deletions clear/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,18 @@ def queuecounters():
command = ["queuestat", "-c"]
run_command(command)

@cli.command()
def fabriccountersqueue():
"""Clear fabric queue counters"""
command = "fabricstat -C -q"
run_command(command)

@cli.command()
def fabriccountersport():
"""Clear fabric port counters"""
command = "fabricstat -C"
run_command(command)

@cli.command()
def pfccounters():
"""Clear pfc counters"""
Expand Down
53 changes: 44 additions & 9 deletions scripts/db_migrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,18 +627,24 @@ def migrate_route_table(self):
Handle route table migration. Migrations handled:
1. 'weight' attr in ROUTE object was introduced 202205 onwards.
Upgrade from older branch to 202205 will require this 'weight' attr to be added explicitly
2. 'protocol' attr in ROUTE introduced in 202305 onwards.
WarmRestartHelper reconcile logic requires to have "protocol" field in the old dumped ROUTE_TABLE.
"""
route_table = self.appDB.get_table("ROUTE_TABLE")
for route_prefix, route_attr in route_table.items():
if type(route_prefix) == tuple:
# IPv6 route_prefix is returned from db as tuple
route_key = "ROUTE_TABLE:" + ":".join(route_prefix)
else:
# IPv4 route_prefix is returned from db as str
route_key = "ROUTE_TABLE:{}".format(route_prefix)

if 'weight' not in route_attr:
if type(route_prefix) == tuple:
# IPv6 route_prefix is returned from db as tuple
route_key = "ROUTE_TABLE:" + ":".join(route_prefix)
else:
# IPv4 route_prefix is returned from db as str
route_key = "ROUTE_TABLE:{}".format(route_prefix)
self.appDB.set(self.appDB.APPL_DB, route_key, 'weight','')

if 'protocol' not in route_attr:
self.appDB.set(self.appDB.APPL_DB, route_key, 'protocol', '')

def update_edgezone_aggregator_config(self):
"""
Update cable length configuration in ConfigDB for T0 neighbor interfaces
Expand Down Expand Up @@ -683,6 +689,20 @@ def update_edgezone_aggregator_config(self):
# Set new cable length values
self.configDB.set(self.configDB.CONFIG_DB, "CABLE_LENGTH|AZURE", intf, EDGEZONE_AGG_CABLE_LENGTH)

def migrate_config_db_flex_counter_delay_status(self):
"""
Migrate "FLEX_COUNTER_TABLE|*": { "value": { "FLEX_COUNTER_DELAY_STATUS": "false" } }
Set FLEX_COUNTER_DELAY_STATUS true in case of fast-reboot
"""

flex_counter_objects = self.configDB.get_keys('FLEX_COUNTER_TABLE')
for obj in flex_counter_objects:
flex_counter = self.configDB.get_entry('FLEX_COUNTER_TABLE', obj)
delay_status = flex_counter.get('FLEX_COUNTER_DELAY_STATUS')
if delay_status is None or delay_status == 'false':
flex_counter['FLEX_COUNTER_DELAY_STATUS'] = 'true'
self.configDB.mod_entry('FLEX_COUNTER_TABLE', obj, flex_counter)

def version_unknown(self):
"""
version_unknown tracks all SONiC versions that doesn't have a version
Expand Down Expand Up @@ -983,17 +1003,32 @@ def version_4_0_2(self):
Version 4_0_2.
"""
log.log_info('Handling version_4_0_2')
self.migrate_config_db_switchport_mode()
self.set_version('version_4_0_2')

if self.stateDB.keys(self.stateDB.STATE_DB, "FAST_REBOOT|system"):
self.migrate_config_db_flex_counter_delay_status()

self.set_version('version_4_0_3')
return 'version_4_0_3'

def version_4_0_3(self):
"""
Version 4_0_3.
This is the latest version for master branch
"""
log.log_info('Handling version_4_0_3')

self.migrate_config_db_switchport_mode()

self.set_version('version_4_0_4')
return 'version_4_0_4'

def version_4_0_4(self):
"""
Version 4_0_4.
This is the latest version for master branch
"""
log.log_info('Handling version_4_0_4')
return None


def get_version(self):
version = self.configDB.get_entry(self.TABLE_NAME, self.TABLE_KEY)
Expand Down
134 changes: 127 additions & 7 deletions scripts/fabricstat
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import argparse
from collections import OrderedDict, namedtuple
import json
import os
import sys

from utilities_common import constants
from utilities_common.cli import json_serial, UserCache
from utilities_common.netstat import format_number_with_comma, table_as_json, ns_diff, format_prate
from natsort import natsorted
from tabulate import tabulate
from sonic_py_common import multi_asic
Expand All @@ -32,6 +35,10 @@ FABRIC_PORT_STATUS_TABLE_PREFIX = APP_FABRIC_PORT_TABLE_NAME+"|"
FABRIC_PORT_STATUS_FIELD = "STATUS"
STATUS_NA = 'N/A'

cnstat_dir = 'N/A'
cnstat_fqn_file_port = 'N/A'
cnstat_fqn_file_queue = 'N/A'

class FabricStat(object):
def __init__(self, namespace):
self.db = None
Expand Down Expand Up @@ -78,6 +85,12 @@ class FabricStat(object):
"""
assert False, 'Need to override this method'

def save_fresh_stats(self):
"""
Get stat for each port and save.
"""
assert False, 'Need to override this method'

def cnstat_print(self, cnstat_dict, errors_only=False):
"""
Print the counter stat.
Expand Down Expand Up @@ -115,6 +128,24 @@ class FabricPortStat(FabricStat):
cnstat_dict[port_name] = PortStat._make(cntr)
return cnstat_dict

def save_fresh_stats(self):
# Get stat for each port and save
counter_port_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_FABRIC_PORT_NAME_MAP)
if counter_port_name_map is None:
return
cnstat_dict = self.get_cnstat()
asic_name = '0'
if self.namespace:
asic_name = multi_asic.get_asic_id_from_name(self.namespace)
try:
cnstat_fqn_file_port_name = cnstat_fqn_file_port + asic_name
json.dump(cnstat_dict, open(cnstat_fqn_file_port_name, 'w'), default=json_serial)
except IOError as e:
print(e.errno, e)
sys.exit(e.errno)
else:
print("Clear and update saved counters port")

def cnstat_print(self, cnstat_dict, errors_only=False):
if len(cnstat_dict) == 0:
print("Counters %s empty" % self.namespace)
Expand All @@ -127,19 +158,44 @@ class FabricPortStat(FabricStat):
asic_name = '0'
if self.namespace:
asic_name = multi_asic.get_asic_id_from_name(self.namespace)

cnstat_fqn_file_port_name = cnstat_fqn_file_port + asic_name
cnstat_cached_dict = {}
if os.path.isfile(cnstat_fqn_file_port_name):
try:
cnstat_cached_dict = json.load(open(cnstat_fqn_file_port_name, 'r'))
except IOError as e:
print(e.errno, e)

for key, data in cnstat_dict.items():
port_id = key[len(PORT_NAME_PREFIX):]
port_name = "PORT" + port_id
# The content in the for each port:
# "IN_CELL, IN_OCTET, OUT_CELL, OUT_OCTET, CRC, FEC_CORRECTABLE, FEC_UNCORRECTABL, SYMBOL_ERR"
# e.g. PORT76 ['0', '0', '36', '6669', '0', '13', '302626', '3']
# Now, set default saved values to 0
diff_cached = ['0', '0', '0', '0', '0', '0', '0', '0']
if port_name in cnstat_cached_dict:
diff_cached = cnstat_cached_dict.get(port_name)

if errors_only:
header = portstat_header_errors_only
table.append((asic_name, port_id, self.get_port_state(key),
data.crc, data.fec_correctable, data.fec_uncorrectable,
data.symbol_err))
ns_diff(data.crc, diff_cached[4]),
ns_diff(data.fec_correctable, diff_cached[5]),
ns_diff(data.fec_uncorrectable, diff_cached[6]),
ns_diff(data.symbol_err, diff_cached[7])))
else:
header = portstat_header_all
table.append((asic_name, port_id, self.get_port_state(key),
data.in_cell, data.in_octet, data.out_cell, data.out_octet,
data.crc, data.fec_correctable, data.fec_uncorrectable,
data.symbol_err))
ns_diff(data.in_cell, diff_cached[0]),
ns_diff(data.in_octet, diff_cached[1]),
ns_diff(data.out_cell, diff_cached[2]),
ns_diff(data.out_octet, diff_cached[3]),
ns_diff(data.crc, diff_cached[4]),
ns_diff(data.fec_correctable, diff_cached[5]),
ns_diff(data.fec_uncorrectable, diff_cached[6]),
ns_diff(data.symbol_err, diff_cached[7])))

print(tabulate(table, header, tablefmt='simple', stralign='right'))
print()
Expand All @@ -166,6 +222,24 @@ class FabricQueueStat(FabricStat):
cnstat_dict[port_queue_name] = QueueStat._make(cntr)
return cnstat_dict

def save_fresh_stats(self):
# Get stat for each port and save
counter_port_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_FABRIC_PORT_NAME_MAP)
if counter_port_name_map is None:
return
cnstat_dict = self.get_cnstat()
asic_name = '0'
if self.namespace:
asic_name = multi_asic.get_asic_id_from_name(self.namespace)
try:
cnstat_fqn_file_queue_name = cnstat_fqn_file_queue + asic_name
json.dump(cnstat_dict, open(cnstat_fqn_file_queue_name, 'w'), default=json_serial)
except IOError as e:
print(e.errno, e)
sys.exit(e.errno)
else:
print("Clear and update saved counters queue")

def cnstat_print(self, cnstat_dict, errors_only=False):
if len(cnstat_dict) == 0:
print("Counters %s empty" % self.namespace)
Expand All @@ -177,11 +251,29 @@ class FabricQueueStat(FabricStat):
asic_name = '0'
if self.namespace:
asic_name = multi_asic.get_asic_id_from_name(self.namespace)

cnstat_fqn_file_queue_name = cnstat_fqn_file_queue + asic_name
cnstat_cached_dict={}
if os.path.isfile(cnstat_fqn_file_queue_name):
try:
cnstat_cached_dict = json.load(open(cnstat_fqn_file_queue_name, 'r'))
except IOError as e:
print(e.errno, e)

for key, data in cnstat_dict.items():
port_name, queue_id = key.split(':')
# The content of saved counters queue for each port:
# portName:queueId CURRENT_LEVEL, WATERMARK_LEVEL, CURRENT_BYTE
# e.g. PORT90:0 ['N/A', 'N/A', 'N/A']
# Now, set default saved values to 0
diff_cached = ['0', '0', '0']
if key in cnstat_cached_dict:
diff_cached = cnstat_cached_dict.get(key)
port_id = port_name[len(PORT_NAME_PREFIX):]
table.append((asic_name, port_id, self.get_port_state(port_name), queue_id,
data.curbyte, data.curlevel, data.watermarklevel))
ns_diff(data.curbyte, diff_cached[2]),
ns_diff(data.curlevel, diff_cached[0]),
ns_diff(data.watermarklevel, diff_cached[1])))

print(tabulate(table, queuestat_header, tablefmt='simple', stralign='right'))
print()
Expand Down Expand Up @@ -214,6 +306,10 @@ class FabricReachability(FabricStat):
return

def main():
global cnstat_dir
global cnstat_fqn_file_port
global cnstat_fqn_file_queue

parser = argparse.ArgumentParser(description='Display the fabric port state and counters',
formatter_class=argparse.RawTextHelpFormatter,
epilog="""
Expand All @@ -223,19 +319,40 @@ Examples:
fabricstat -p -n asic0 -e
fabricstat -q
fabricstat -q -n asic0
fabricstat -C
fabricstat -D
""")

parser.add_argument('-q','--queue', action='store_true', help='Display fabric queue stat, otherwise port stat')
parser.add_argument('-r','--reachability', action='store_true', help='Display reachability, otherwise port stat')
parser.add_argument('-n','--namespace', default=None, help='Display fabric ports counters for specific namespace')
parser.add_argument('-e', '--errors', action='store_true', help='Display errors')
parser.add_argument('-C','--clear', action='store_true', help='Copy & clear fabric counters')
parser.add_argument('-D','--delete', action='store_true', help='Delete saved stats')

args = parser.parse_args()
queue = args.queue
reachability = args.reachability
namespace = args.namespace
errors_only = args.errors

save_fresh_stats = args.clear
delete_stats = args.delete

cache = UserCache()

cnstat_dir = cache.get_directory()

cnstat_file = "fabricstatport"
cnstat_fqn_file_port = os.path.join(cnstat_dir, cnstat_file)

cnstat_file = "fabricstatqueue"
cnstat_fqn_file_queue = os.path.join(cnstat_dir, cnstat_file)

if delete_stats:
cache.remove()
sys.exit(0)

def nsStat(ns, errors_only):
if queue:
stat = FabricQueueStat(ns)
Expand All @@ -246,7 +363,10 @@ Examples:
else:
stat = FabricPortStat(ns)
cnstat_dict = stat.get_cnstat_dict()
stat.cnstat_print(cnstat_dict, errors_only)
if save_fresh_stats:
stat.save_fresh_stats()
else:
stat.cnstat_print(cnstat_dict, errors_only)

if namespace is None:
# All asics or all fabric asics
Expand Down
4 changes: 2 additions & 2 deletions scripts/portstat
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ class Portstat(object):
print(table_as_json(table, header))
else:
print(tabulate(table, header, tablefmt='simple', stralign='right'))
if multi_asic.is_multi_asic() or device_info.is_chassis() and not use_json:
if (multi_asic.is_multi_asic() or device_info.is_chassis()) and not use_json:
print("\nReminder: Please execute 'show interface counters -d all' to include internal links\n")

def cnstat_intf_diff_print(self, cnstat_new_dict, cnstat_old_dict, intf_list):
Expand Down Expand Up @@ -556,7 +556,7 @@ class Portstat(object):
print(table_as_json(table, header))
else:
print(tabulate(table, header, tablefmt='simple', stralign='right'))
if multi_asic.is_multi_asic() or device_info.is_chassis() and not use_json:
if (multi_asic.is_multi_asic() or device_info.is_chassis()) and not use_json:
print("\nReminder: Please execute 'show interface counters -d all' to include internal links\n")

def main():
Expand Down
6 changes: 3 additions & 3 deletions scripts/sfpshow
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,9 @@ class SFPShow(object):
channel_threshold_align = 18
module_threshold_align = 15

if sfp_type.startswith('QSFP'):
if sfp_type.startswith('QSFP') or sfp_type.startswith('OSFP'):
# Channel Monitor
if sfp_type.startswith('QSFP-DD'):
if sfp_type.startswith('QSFP-DD') or sfp_type.startswith('OSFP'):
output_dom += (indent + 'ChannelMonitorValues:\n')
sorted_key_table = natsorted(QSFP_DD_DOM_CHANNEL_MONITOR_MAP)
output_channel = self.format_dict_value_to_string(
Expand All @@ -365,7 +365,7 @@ class SFPShow(object):
output_dom += output_channel

# Channel Threshold
if sfp_type.startswith('QSFP-DD'):
if sfp_type.startswith('QSFP-DD') or sfp_type.startswith('OSFP'):
dom_map = SFP_DOM_CHANNEL_THRESHOLD_MAP
else:
dom_map = QSFP_DOM_CHANNEL_THRESHOLD_MAP
Expand Down
6 changes: 3 additions & 3 deletions sfputil/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,9 @@ def convert_dom_to_output_string(sfp_type, dom_info_dict):
channel_threshold_align = 18
module_threshold_align = 15

if sfp_type.startswith('QSFP'):
if sfp_type.startswith('QSFP') or sfp_type.startswith('OSFP'):
# Channel Monitor
if sfp_type.startswith('QSFP-DD'):
if sfp_type.startswith('QSFP-DD') or sfp_type.startswith('OSFP'):
output_dom += (indent + 'ChannelMonitorValues:\n')
sorted_key_table = natsorted(QSFP_DD_DOM_CHANNEL_MONITOR_MAP)
output_channel = format_dict_value_to_string(
Expand All @@ -392,7 +392,7 @@ def convert_dom_to_output_string(sfp_type, dom_info_dict):
output_dom += output_channel

# Channel Threshold
if sfp_type.startswith('QSFP-DD'):
if sfp_type.startswith('QSFP-DD') or sfp_type.startswith('OSFP'):
dom_map = SFP_DOM_CHANNEL_THRESHOLD_MAP
else:
dom_map = QSFP_DOM_CHANNEL_THRESHOLD_MAP
Expand Down
Loading

0 comments on commit 7177585

Please sign in to comment.