From 44dbfde5ebfdeb914ac8484247f485fa4a69ac1c Mon Sep 17 00:00:00 2001 From: Alpesh S Patel Date: Wed, 27 Sep 2023 08:40:06 -0700 Subject: [PATCH] mmuconfig to set threshold for profiles master#2775 Signed-off-by: Alpesh S Patel --- config/main.py | 16 +++ scripts/mmuconfig | 45 ++++--- tests/buffer_input/buffer_test_vectors.py | 14 +++ .../mmuconfig_input/mmuconfig_test_vectors.py | 112 ++++++++++++++++++ tests/mmuconfig_test.py | 86 ++++++++++++++ tests/mock_tables/config_db.json | 10 ++ 6 files changed, 268 insertions(+), 15 deletions(-) create mode 100644 tests/mmuconfig_input/mmuconfig_test_vectors.py create mode 100644 tests/mmuconfig_test.py diff --git a/config/main.py b/config/main.py index 3a4e33827..e3cc093d1 100644 --- a/config/main.py +++ b/config/main.py @@ -5677,6 +5677,22 @@ def ecn(profile, rmax, rmin, ymax, ymin, gmax, gmin, rdrop, ydrop, gdrop, verbos clicommon.run_command(command, display_cmd=verbose) +# +# 'mmu' command ('config mmu...') +# +@config.command() +@click.option('-p', metavar='', type=str, required=True, help="Profile name") +@click.option('-a', metavar='', type=click.IntRange(-8,8), help="Set alpha for profile type dynamic") +@click.option('-s', metavar='', type=int, help="Set staticth for profile type static") +def mmu(p, a, s): + """mmuconfig configuration tasks""" + log.log_info("'mmuconfig -p {}' executing...".format(p)) + command = "mmuconfig -p %s" % p + if a is not None: command += " -a %d" % a + if s is not None: command += " -s %d" % s + clicommon.run_command(command) + + # # 'pfc' group ('config interface pfc ...') # diff --git a/scripts/mmuconfig b/scripts/mmuconfig index c0338a176..ebeb74fda 100755 --- a/scripts/mmuconfig +++ b/scripts/mmuconfig @@ -3,7 +3,7 @@ """ mmuconfig is the utility to show and change mmu configuration -usage: mmuconfig [-h] [-v] [-l] [-p PROFILE] [-a ALPHA] [-vv] +usage: mmuconfig [-h] [-v] [-l] [-p PROFILE] [-a ALPHA] [-vv] [-s staticth] optional arguments: -h --help show this help message and exit @@ -12,6 +12,7 @@ optional arguments: -l --list show mmu configuration -p --profile specify buffer profile name -a --alpha set n for dyanmic threshold alpha 2^(n) + -s --staticth set static threshold """ @@ -20,14 +21,17 @@ import sys import argparse import tabulate import traceback +import json BUFFER_POOL_TABLE_NAME = "BUFFER_POOL" BUFFER_PROFILE_TABLE_NAME = "BUFFER_PROFILE" DEFAULT_LOSSLESS_BUFFER_PARAMETER_NAME = "DEFAULT_LOSSLESS_BUFFER_PARAMETER" DYNAMIC_THRESHOLD = "dynamic_th" +STATIC_THRESHOLD = "static_th" BUFFER_PROFILE_FIELDS = { - "alpha": DYNAMIC_THRESHOLD + "alpha": DYNAMIC_THRESHOLD, + "staticth" : STATIC_THRESHOLD } # mock the redis for unit test purposes # @@ -44,18 +48,11 @@ except KeyError: from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector -BUFFER_POOL_TABLE_NAME = "BUFFER_POOL" -BUFFER_PROFILE_TABLE_NAME = "BUFFER_PROFILE" - -DYNAMIC_THRESHOLD = "dynamic_th" -BUFFER_PROFILE_FIELDS = { - "alpha": DYNAMIC_THRESHOLD -} - class MmuConfig(object): - def __init__(self, verbose, config): + def __init__(self, verbose, config, filename): self.verbose = verbose self.config = config + self.filename = filename # Set up db connections if self.config: @@ -120,24 +117,34 @@ class MmuConfig(object): print("No buffer profile information available") def set(self, profile, field_alias, value): - if os.geteuid() != 0: + if os.geteuid() != 0 and os.environ.get("UTILITIES_UNIT_TESTING", "0") != "2": sys.exit("Root privileges required for this operation") field = BUFFER_PROFILE_FIELDS[field_alias] + buf_profs = self.db.get_table(BUFFER_PROFILE_TABLE_NAME) + v = int(value) if field == DYNAMIC_THRESHOLD: - v = int(value) if v < -8 or v > 8: sys.exit("Invalid alpha value: 2^(%s)" % (value)) - buf_profs = self.db.get_table(BUFFER_PROFILE_TABLE_NAME) if profile in buf_profs and DYNAMIC_THRESHOLD not in buf_profs[profile]: sys.exit("%s not using dynamic thresholding" % (profile)) + elif field == STATIC_THRESHOLD: + if v < 0: + sys.exit("Invalid static threshold value: (%s)" % (value)) + + if profile in buf_profs and STATIC_THRESHOLD not in buf_profs[profile]: + sys.exit("%s not using static threshold" % (profile)) else: sys.exit("Set field %s not supported" % (field)) if self.verbose: print("Setting %s %s value to %s" % (profile, field, value)) self.db.mod_entry(BUFFER_PROFILE_TABLE_NAME, profile, {field: value}) + if self.filename is not None: + prof_table = self.db.get_table(BUFFER_PROFILE_TABLE_NAME) + with open(self.filename, "w") as fd: + json.dump(prof_table, fd) def main(config): @@ -148,6 +155,7 @@ def main(config): parser.add_argument('-l', '--list', action='store_true', help='show mmu configuration') parser.add_argument('-p', '--profile', type=str, help='specify buffer profile name', default=None) parser.add_argument('-a', '--alpha', type=str, help='set n for dyanmic threshold alpha 2^(n)', default=None) + parser.add_argument('-s', '--staticth', type=str, help='set static threshold', default=None) parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0') else: parser = argparse.ArgumentParser(description='Show buffer state', @@ -157,16 +165,23 @@ def main(config): parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0') parser.add_argument('-vv', '--verbose', action='store_true', help='verbose output', default=False) + parser.add_argument('-f', '--filename', help='file used by mock tests', type=str, default=None) + + if os.environ.get("UTILITIES_UNIT_TESTING", "0") == "2": + sys.argv.extend(['-f', '/tmp/mmuconfig']) + args = parser.parse_args() try: - mmu_cfg = MmuConfig(args.verbose, config) + mmu_cfg = MmuConfig(args.verbose, config, args.filename) if args.list: mmu_cfg.list() elif config and args.profile: if args.alpha: mmu_cfg.set(args.profile, "alpha", args.alpha) + elif args.staticth: + mmu_cfg.set(args.profile, "staticth", args.staticth) else: parser.print_help() sys.exit(1) diff --git a/tests/buffer_input/buffer_test_vectors.py b/tests/buffer_input/buffer_test_vectors.py index b94d428a3..baa99608a 100644 --- a/tests/buffer_input/buffer_test_vectors.py +++ b/tests/buffer_input/buffer_test_vectors.py @@ -30,6 +30,13 @@ type ingress ---- ------- +Pool: ingress_lossless_pool_hbm +---- --------- +mode static +size 139458240 +type ingress +---- --------- + Profile: ingress_lossy_profile ---------- ------------------ dynamic_th 3 @@ -37,6 +44,13 @@ size 0 ---------- ------------------ +Profile: ingress_lossless_profile_hbm +--------- ------------------------- +static_th 12121212 +pool ingress_lossless_pool_hbm +size 0 +--------- ------------------------- + Profile: headroom_profile ---------- --------------------- dynamic_th 0 diff --git a/tests/mmuconfig_input/mmuconfig_test_vectors.py b/tests/mmuconfig_input/mmuconfig_test_vectors.py new file mode 100644 index 000000000..c20a96451 --- /dev/null +++ b/tests/mmuconfig_input/mmuconfig_test_vectors.py @@ -0,0 +1,112 @@ +show_mmu_config = """\ +Lossless traffic pattern: +-------------------- - +default_dynamic_th 0 +over_subscribe_ratio 2 +-------------------- - + +Pool: egress_lossless_pool +---- -------- +mode dynamic +size 13945824 +type egress +---- -------- + +Pool: egress_lossy_pool +---- ------- +mode dynamic +type egress +---- ------- + +Pool: ingress_lossless_pool +---- ------- +mode dynamic +type ingress +---- ------- + +Pool: ingress_lossy_pool +---- ------- +mode dynamic +type ingress +---- ------- + +Pool: ingress_lossless_pool_hbm +---- --------- +mode static +size 139458240 +type ingress +---- --------- + +Profile: ingress_lossy_profile +---------- ------------------ +dynamic_th 3 +pool ingress_lossy_pool +size 0 +---------- ------------------ + +Profile: ingress_lossless_profile_hbm +--------- ------------------------- +static_th 12121212 +pool ingress_lossless_pool_hbm +size 0 +--------- ------------------------- + +Profile: headroom_profile +---------- --------------------- +dynamic_th 0 +pool ingress_lossless_pool +xon 18432 +xoff 32768 +size 51200 +---------- --------------------- + +Profile: alpha_profile +------------- --------------------- +dynamic_th 0 +pool ingress_lossless_pool +headroom_type dynamic +------------- --------------------- + +Profile: egress_lossless_profile +---------- -------------------- +dynamic_th 0 +pool egress_lossless_pool +size 0 +---------- -------------------- + +Profile: egress_lossy_profile +---------- ----------------- +dynamic_th 0 +pool egress_lossy_pool +size 0 +---------- ----------------- + +""" + +testData = { + 'mmuconfig_list' : {'cmd' : ['show'], + 'args' : [], + 'rc' : 0, + 'rc_output': show_mmu_config + }, + 'mmu_cfg_static_th' : {'cmd' : ['config'], + 'args' : ['-p', 'ingress_lossless_profile_hbm', '-s', '12121213'], + 'rc' : 0, + 'db_table' : 'BUFFER_PROFILE', + 'cmp_args' : ['ingress_lossless_profile_hbm,static_th,12121213'], + 'rc_msg' : '' + }, + 'mmu_cfg_alpha' : {'cmd' : ['config'], + 'args' : ['-p', 'alpha_profile', '-a', '2'], + 'rc' : 0, + 'db_table' : 'BUFFER_PROFILE', + 'cmp_args' : ['alpha_profile,dynamic_th,2'], + 'rc_msg' : '' + }, + 'mmu_cfg_alpha_invalid' : {'cmd' : ['config'], + 'args' : ['-p', 'alpha_profile', '-a', '12'], + 'rc' : 2, + 'rc_msg' : 'Usage: mmu [OPTIONS]\nTry "mmu --help" for help.\n\nError: Invalid value for "-a": 12 is not in the valid range of -8 to 8.\n' + } + + } diff --git a/tests/mmuconfig_test.py b/tests/mmuconfig_test.py new file mode 100644 index 000000000..7218270e3 --- /dev/null +++ b/tests/mmuconfig_test.py @@ -0,0 +1,86 @@ +import os +import sys +import json +import pytest + +from click.testing import CliRunner +import config.main as config +import show.main as show +from utilities_common.db import Db +from .mmuconfig_input.mmuconfig_test_vectors import * + +test_path = os.path.dirname(os.path.abspath(__file__)) +modules_path = os.path.dirname(test_path) +scripts_path = os.path.join(modules_path, "scripts") +sys.path.insert(0, test_path) +sys.path.insert(0, modules_path) + + +class Testmmuconfig(object): + @classmethod + def setup_class(cls): + os.environ["PATH"] += os.pathsep + scripts_path + os.environ['UTILITIES_UNIT_TESTING'] = "2" + print("SETUP") + + def test_mmu_show_config(self): + self.executor(testData['mmuconfig_list']) + + def test_mmu_alpha_config(self): + self.executor(testData['mmu_cfg_alpha']) + + def test_mmu_alpha_invalid_config(self): + self.executor(testData['mmu_cfg_alpha_invalid']) + + def test_mmu_staticth_config(self): + self.executor(testData['mmu_cfg_static_th']) + + def executor(self, input): + runner = CliRunner() + + if 'db_table' in input: + db = Db() + data_list = list(db.cfgdb.get_table(input['db_table'])) + input['rc_msg'] = input['rc_msg'].format(",".join(data_list)) + + if 'show' in input['cmd']: + exec_cmd = show.cli.commands["mmu"] + result = runner.invoke(exec_cmd, input['args']) + exit_code = result.exit_code + output = result.output + elif 'config' in input['cmd']: + exec_cmd = config.config.commands["mmu"] + result = runner.invoke(exec_cmd, input['args'], catch_exceptions=False) + exit_code = result.exit_code + output = result.output + + print(exit_code) + print(output) + + if input['rc'] == 0: + assert exit_code == 0 + else: + assert exit_code != 0 + + if 'cmp_args' in input: + fd = open('/tmp/mmuconfig', 'r') + cmp_data = json.load(fd) + for args in input['cmp_args']: + profile, name, value = args.split(',') + assert(cmp_data[profile][name] == value) + fd.close() + + if 'rc_msg' in input: + assert input['rc_msg'] in output + + if 'rc_output' in input: + assert output == input['rc_output'] + + + @classmethod + def teardown_class(cls): + os.environ["PATH"] = os.pathsep.join(os.environ["PATH"].split(os.pathsep)[:-1]) + os.environ['UTILITIES_UNIT_TESTING'] = "0" + if os.path.isfile('/tmp/mmuconfig'): + os.remove('/tmp/mmuconfig') + print("TEARDOWN") diff --git a/tests/mock_tables/config_db.json b/tests/mock_tables/config_db.json index 88d896bd9..7a5ce8b92 100644 --- a/tests/mock_tables/config_db.json +++ b/tests/mock_tables/config_db.json @@ -1868,11 +1868,21 @@ "mode": "dynamic", "type": "ingress" }, + "BUFFER_POOL|ingress_lossless_pool_hbm": { + "mode": "static", + "size": "139458240", + "type": "ingress" + }, "BUFFER_PROFILE|ingress_lossy_profile": { "dynamic_th": "3", "pool": "ingress_lossy_pool", "size": "0" }, + "BUFFER_PROFILE|ingress_lossless_profile_hbm": { + "static_th": "12121212", + "pool": "ingress_lossless_pool_hbm", + "size": "0" + }, "BUFFER_PROFILE|headroom_profile": { "dynamic_th": "0", "pool": "ingress_lossless_pool",