Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
MuhammadUmarAsad committed Oct 20, 2022
2 parents 8358561 + d973032 commit 1d9daf1
Show file tree
Hide file tree
Showing 22 changed files with 2,169 additions and 86 deletions.
2 changes: 1 addition & 1 deletion config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2649,7 +2649,7 @@ def reload(ctx, no_dynamic_buffer, dry_run, json_data, ports):
namespace_list = multi_asic.get_namespaces_from_linux()

buffer_model_updated = False
vendors_supporting_dynamic_buffer = ["mellanox"]
vendors_supporting_dynamic_buffer = ["mellanox", "barefoot"]

for ns in namespace_list:
if ns is DEFAULT_NAMESPACE:
Expand Down
117 changes: 62 additions & 55 deletions config/validated_config_db_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,59 +5,66 @@
from generic_config_updater.generic_updater import GenericUpdater, ConfigFormat
from generic_config_updater.gu_common import EmptyTableError, genericUpdaterLogging

def ValidatedConfigDBConnector(config_db_connector):
yang_enabled = device_info.is_yang_config_validation_enabled(config_db_connector)
if yang_enabled:
config_db_connector.set_entry = validated_set_entry
config_db_connector.delete_table = validated_delete_table
return config_db_connector

def make_path_value_jsonpatch_compatible(table, key, value):
if type(key) == tuple:
path = JsonPointer.from_parts([table, '|'.join(key)]).path
else:
path = JsonPointer.from_parts([table, key]).path
if value == {"NULL" : "NULL"}:
value = {}
return path, value

def create_gcu_patch(op, table, key=None, value=None):
if key:
path, value = make_path_value_jsonpatch_compatible(table, key, value)
else:
path = "/{}".format(table)

gcu_json_input = []
gcu_json = {"op": "{}".format(op),
"path": "{}".format(path)}
if op == "add":
gcu_json["value"] = value

gcu_json_input.append(gcu_json)
gcu_patch = jsonpatch.JsonPatch(gcu_json_input)
return gcu_patch

def validated_delete_table(table):
gcu_patch = create_gcu_patch("remove", table)
format = ConfigFormat.CONFIGDB.name
config_format = ConfigFormat[format.upper()]
try:
GenericUpdater().apply_patch(patch=gcu_patch, config_format=config_format, verbose=False, dry_run=False, ignore_non_yang_tables=False, ignore_paths=None)
except ValueError as e:
logger = genericUpdaterLogging.get_logger(title="Patch Applier", print_all_to_console=True)
logger.log_notice("Unable to remove entry, as doing so will result in invalid config. Error: {}".format(e))

def validated_set_entry(table, key, value):
if value is not None:
op = "add"
else:
op = "remove"
class ValidatedConfigDBConnector(object):

gcu_patch = create_gcu_patch(op, table, key, value)
format = ConfigFormat.CONFIGDB.name
config_format = ConfigFormat[format.upper()]

try:
GenericUpdater().apply_patch(patch=gcu_patch, config_format=config_format, verbose=False, dry_run=False, ignore_non_yang_tables=False, ignore_paths=None)
except EmptyTableError:
validated_delete_table(table)
def __init__(self, config_db_connector):
self.connector = config_db_connector
self.yang_enabled = device_info.is_yang_config_validation_enabled(self.connector)

def __getattr__(self, name):
if self.yang_enabled:
if name == "set_entry":
return self.validated_set_entry
if name == "delete_table":
return self.validated_delete_table
return self.connector.__getattribute__(name)

def make_path_value_jsonpatch_compatible(self, table, key, value):
if type(key) == tuple:
path = JsonPointer.from_parts([table, '|'.join(key)]).path
else:
path = JsonPointer.from_parts([table, key]).path
if value == {"NULL" : "NULL"}:
value = {}
return path, value

def create_gcu_patch(self, op, table, key=None, value=None):
if key:
path, value = self.make_path_value_jsonpatch_compatible(table, key, value)
else:
path = "/{}".format(table)

gcu_json_input = []
gcu_json = {"op": "{}".format(op),
"path": "{}".format(path)}
if op == "add":
gcu_json["value"] = value

gcu_json_input.append(gcu_json)
gcu_patch = jsonpatch.JsonPatch(gcu_json_input)
return gcu_patch

def validated_delete_table(self, table):
gcu_patch = self.create_gcu_patch("remove", table)
format = ConfigFormat.CONFIGDB.name
config_format = ConfigFormat[format.upper()]
try:
GenericUpdater().apply_patch(patch=gcu_patch, config_format=config_format, verbose=False, dry_run=False, ignore_non_yang_tables=False, ignore_paths=None)
except ValueError as e:
logger = genericUpdaterLogging.get_logger(title="Patch Applier", print_all_to_console=True)
logger.log_notice("Unable to remove entry, as doing so will result in invalid config. Error: {}".format(e))

def validated_set_entry(self, table, key, value):
if value is not None:
op = "add"
else:
op = "remove"

gcu_patch = self.create_gcu_patch(op, table, key, value)
format = ConfigFormat.CONFIGDB.name
config_format = ConfigFormat[format.upper()]

try:
GenericUpdater().apply_patch(patch=gcu_patch, config_format=config_format, verbose=False, dry_run=False, ignore_non_yang_tables=False, ignore_paths=None)
except EmptyTableError:
self.validated_delete_table(table)
33 changes: 33 additions & 0 deletions doc/Command-Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -10611,6 +10611,39 @@ This command removes all unused images from the device, leaving only the current
No image(s) to remove
```
**sonic-installer get-fips**
This command is used to get the fips status.
- Usage:
```
sonic-installer get-fips [<image_name>]
```
If the image is not specified, the next boot image will be used.
- Example:
```
admin@sonic:~$ sudo sonic-installer get-fips
FIPS is enabled
```
**sonic-installer set-fips**
The command is used to enable or disable the fips.
- Usage:
```
sonic-installer set-fips [--enable-fips|--disable-fips] [<image_name>]
```
If the option --enable-fips or --disable-fips is not specified, the default behavior is to enable fips.
- Example:
```
admin@sonic:~$ sudo sonic-installer set-fips --enable-fips
Done
Set FIPS for the image successfully
```
Go Back To [Beginning of the document](#) or [Beginning of this section](#software-installation-and-management)
Expand Down
217 changes: 217 additions & 0 deletions scripts/fabricstat
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
#!/usr/bin/env python3

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

from utilities_common import constants
from natsort import natsorted
from tabulate import tabulate
from sonic_py_common import multi_asic
from swsscommon.swsscommon import APP_FABRIC_PORT_TABLE_NAME, COUNTERS_TABLE, COUNTERS_FABRIC_PORT_NAME_MAP, COUNTERS_FABRIC_QUEUE_NAME_MAP
import utilities_common.multi_asic as multi_asic_util

# 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
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_NAME_PREFIX = 'PORT'
COUNTER_TABLE_PREFIX = COUNTERS_TABLE+":"
FABRIC_PORT_STATUS_TABLE_PREFIX = APP_FABRIC_PORT_TABLE_NAME+"|"
FABRIC_PORT_STATUS_FIELD = "STATUS"
STATUS_NA = 'N/A'

class FabricStat(object):
def __init__(self, namespace):
self.db = None
self.namespace = namespace
self.multi_asic = multi_asic_util.MultiAsic(constants.DISPLAY_ALL, namespace)

def get_cnstat_dict(self):
self.cnstat_dict = OrderedDict()
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_port_state(self, port_name):
"""
Get the port state
"""
full_table_id = FABRIC_PORT_STATUS_TABLE_PREFIX + port_name
oper_state = self.db.get(self.db.STATE_DB, full_table_id, FABRIC_PORT_STATUS_FIELD)
if oper_state is not None:
return oper_state
return STATUS_NA

def get_counters(self, counter_bucket_dict, table_id):
fields = ["0"] * len(counter_bucket_dict)
for pos, counter_name in counter_bucket_dict.items():
full_table_id = COUNTER_TABLE_PREFIX + table_id
counter_data = self.db.get(self.db.COUNTERS_DB, full_table_id, counter_name)
if counter_data is None:
fields[pos] = STATUS_NA
elif fields[pos] != STATUS_NA:
fields[pos] = str(int(fields[pos]) + int(counter_data))
return fields

def get_cnstat(self):
"""
Get the counters info from database.
"""
assert False, 'Need to override this method'

def cnstat_print(self, cnstat_dict, errors_only=False):
"""
Print the counter stat.
"""
assert False, 'Need to override this method'

PortStat = namedtuple("PortStat", "in_cell, in_octet, out_cell, out_octet,\
crc, fec_correctable, fec_uncorrectable, symbol_err")
port_counter_bucket_list = [
'SAI_PORT_STAT_IF_IN_FABRIC_DATA_UNITS',
'SAI_PORT_STAT_IF_IN_OCTETS',
'SAI_PORT_STAT_IF_OUT_FABRIC_DATA_UNITS',
'SAI_PORT_STAT_IF_OUT_OCTETS',
'SAI_PORT_STAT_IF_IN_ERRORS',
'SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES',
'SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES',
'SAI_PORT_STAT_IF_IN_FEC_SYMBOL_ERRORS',
]
port_counter_bucket_dict = {k : v for k, v in enumerate(port_counter_bucket_list)}

portstat_header_all = ['ASIC', 'PORT', 'STATE',
'IN_CELL', 'IN_OCTET', 'OUT_CELL', 'OUT_OCTET',
'CRC', 'FEC_CORRECTABLE', 'FEC_UNCORRECTABLE', 'SYMBOL_ERR']
portstat_header_errors_only = ['ASIC', 'PORT', 'STATE',
'CRC', 'FEC_CORRECTABLE', 'FEC_UNCORRECTABLE', 'SYMBOL_ERR']

class FabricPortStat(FabricStat):
def get_cnstat(self):
counter_port_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_FABRIC_PORT_NAME_MAP)
cnstat_dict = OrderedDict()
if counter_port_name_map is None:
return cnstat_dict
for port_name in natsorted(counter_port_name_map):
cntr = self.get_counters(port_counter_bucket_dict, counter_port_name_map[port_name])
cnstat_dict[port_name] = PortStat._make(cntr)
return cnstat_dict

def cnstat_print(self, cnstat_dict, errors_only=False):
if len(cnstat_dict) == 0:
print("Counters %s empty" % self.namespace)
return

table = []
header = None
asic = multi_asic.get_asic_id_from_name(self.namespace)
for key, data in cnstat_dict.items():
port_id = key[len(PORT_NAME_PREFIX):]
if errors_only:
header = portstat_header_errors_only
table.append((asic, port_id, self.get_port_state(key),
data.crc, data.fec_correctable, data.fec_uncorrectable,
data.symbol_err))
else:
header = portstat_header_all
table.append((asic, 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))

print(tabulate(table, header, tablefmt='simple', stralign='right'))
print()

QueueStat = namedtuple("QueueStat", "curlevel, watermarklevel, curbyte")

queue_counter_bucket_list = [
'SAI_QUEUE_STAT_CURR_OCCUPANCY_LEVEL',
'SAI_QUEUE_STAT_WATERMARK_LEVEL',
'SAI_QUEUE_STAT_CURR_OCCUPANCY_BYTES',
]
queue_counter_bucket_dict = {k : v for k, v in enumerate(queue_counter_bucket_list)}

queuestat_header = ['ASIC', 'PORT', 'STATE', 'QUEUE_ID', 'CURRENT_BYTE', 'CURRENT_LEVEL', 'WATERMARK_LEVEL']

class FabricQueueStat(FabricStat):
def get_cnstat(self):
counter_queue_name_map = self.db.get_all(self.db.COUNTERS_DB, COUNTERS_FABRIC_QUEUE_NAME_MAP)
cnstat_dict = OrderedDict()
if counter_queue_name_map is None:
return cnstat_dict
for port_queue_name in natsorted(counter_queue_name_map):
cntr = self.get_counters(queue_counter_bucket_dict, counter_queue_name_map[port_queue_name])
cnstat_dict[port_queue_name] = QueueStat._make(cntr)
return cnstat_dict

def cnstat_print(self, cnstat_dict, errors_only=False):
if len(cnstat_dict) == 0:
print("Counters %s empty" % self.namespace)
return

table = []
asic = multi_asic.get_asic_id_from_name(self.namespace)
for key, data in cnstat_dict.items():
port_name, queue_id = key.split(':')
port_id = port_name[len(PORT_NAME_PREFIX):]
table.append((asic, port_id, self.get_port_state(port_name), queue_id,
data.curbyte, data.curlevel, data.watermarklevel))

print(tabulate(table, queuestat_header, tablefmt='simple', stralign='right'))
print()

def main():
parser = argparse.ArgumentParser(description='Display the fabric port state and counters',
formatter_class=argparse.RawTextHelpFormatter,
epilog="""
Examples:
fabricstat
fabricstat --namespace asic0
fabricstat -p -n asic0 -e
fabricstat -q
fabricstat -q -n asic0
""")

parser.add_argument('-q','--queue', action='store_true', help='Display fabric queue stat, 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')

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

def nsStat(ns, errors_only):
stat = FabricQueueStat(ns) if queue else FabricPortStat(ns)
cnstat_dict = stat.get_cnstat_dict()
stat.cnstat_print(cnstat_dict, errors_only)

if namespace is None:
# All asics or all fabric asics
multi_asic = multi_asic_util.MultiAsic()
for ns in multi_asic.get_ns_list_based_on_options():
nsStat(ns, errors_only)
else:
# Asic with namespace
nsStat(namespace, errors_only)

if __name__ == "__main__":
main()
Loading

0 comments on commit 1d9daf1

Please sign in to comment.