diff --git a/config/syslog.py b/config/syslog.py index 90fc52ec9d..7533a7f71f 100644 --- a/config/syslog.py +++ b/config/syslog.py @@ -471,3 +471,86 @@ def rate_limit_container(db, service_name, interval, burst): feature_data = db.cfgdb.get_table(syslog_common.FEATURE_TABLE) syslog_common.service_validator(feature_data, service_name) syslog_common.save_rate_limit_to_db(db, service_name, interval, burst, log) + + +@syslog.group( + name="rate-limit-feature", + cls=clicommon.AliasedGroup +) +def rate_limit_feature(): + """ Configure syslog rate limit feature """ + pass + + +@rate_limit_feature.command("enable") +@clicommon.pass_db +def enable_rate_limit_feature(db): + """ Enable syslog rate limit feature """ + feature_data = db.cfgdb.get_table(syslog_common.FEATURE_TABLE) + for feature_name in feature_data.keys(): + click.echo(f'Enabling syslog rate limit feature for {feature_name}') + output, _ = clicommon.run_command(['docker', 'ps', '-q', '-f', 'status=running', '-f', f'name={feature_name}'], return_cmd=True) + if not output: + click.echo(f'{feature_name} is not running, ignoring...') + continue + + output, _ = clicommon.run_command(['docker', 'exec', '-i', feature_name, 'supervisorctl', 'status', 'containercfgd'], + ignore_error=True, return_cmd=True) + if 'no such process' not in output: + click.echo(f'Syslog rate limit feature is already enabled in {feature_name}, ignoring...') + continue + + commands = [ + ['docker', 'cp', '/usr/share/sonic/templates/containercfgd.conf', f'{feature_name}:/etc/supervisor/conf.d/'], + ['docker', 'exec', '-i', feature_name, 'supervisorctl', 'reread'], + ['docker', 'exec', '-i', feature_name, 'supervisorctl', 'update'], + ['docker', 'exec', '-i', feature_name, 'supervisorctl', 'start', 'containercfgd'] + ] + + failed = False + for command in commands: + output, ret = clicommon.run_command(command, return_cmd=True) + if ret != 0: + failed = True + click.echo(f'Enable syslog rate limit feature for {feature_name} failed - {output}') + break + + if not failed: + click.echo(f'Enabled syslog rate limit feature for {feature_name}') + + +@rate_limit_feature.command("disable") +@clicommon.pass_db +def disable_rate_limit_feature(db): + """ Disable syslog rate limit feature """ + feature_data = db.cfgdb.get_table(syslog_common.FEATURE_TABLE) + for feature_name in feature_data.keys(): + click.echo(f'Disabling syslog rate limit feature for {feature_name}') + output, _ = clicommon.run_command(['docker', 'ps', '-q', '-f', 'status=running', '-f', f'name={feature_name}'], return_cmd=True) + if not output: + click.echo(f'{feature_name} is not running, ignoring...') + continue + + output, _ = clicommon.run_command(['docker', 'exec', '-i', feature_name, 'supervisorctl', 'status', 'containercfgd'], + ignore_error=True, return_cmd=True) + if 'no such process' in output: + click.echo(f'Syslog rate limit feature is already disabled in {feature_name}, ignoring...') + continue + + commands = [ + ['docker', 'exec', '-i', feature_name, 'supervisorctl', 'stop', 'containercfgd'], + ['docker', 'exec', '-i', feature_name, 'rm', '-f', '/etc/supervisor/conf.d/containercfgd.conf'], + ['docker', 'exec', '-i', feature_name, 'supervisorctl', 'reread'], + ['docker', 'exec', '-i', feature_name, 'supervisorctl', 'update'] + ] + failed = False + for command in commands: + output, ret = clicommon.run_command(command, return_cmd=True) + if ret != 0: + failed = True + click.echo(f'Disable syslog rate limit feature for {feature_name} failed - {output}') + break + + if not failed: + click.echo(f'Disabled syslog rate limit feature for {feature_name}') + diff --git a/doc/Command-Reference.md b/doc/Command-Reference.md index 39e76d0b56..84d7fb92db 100644 --- a/doc/Command-Reference.md +++ b/doc/Command-Reference.md @@ -9855,6 +9855,33 @@ This command is used to configure syslog rate limit for containers. admin@sonic:~$ sudo config syslog rate-limit-container bgp --interval 300 --burst 20000 ``` +**config syslog rate-limit-feature enable** + +This command is used to enable syslog rate limit feature. + +- Usage: + ``` + config syslog rate-limit-feature enable + ``` + +- Example: + ``` + admin@sonic:~$ sudo config syslog rate-limit-feature enable + ``` + +**config syslog rate-limit-feature disable** + +This command is used to disable syslog rate limit feature. + +- Usage: + ``` + config syslog rate-limit-feature disable + ``` + +- Example: + ``` + admin@sonic:~$ sudo config syslog rate-limit-feature disable + ``` Go Back To [Beginning of the document](#) or [Beginning of this section](#syslog) diff --git a/generic_config_updater/field_operation_validators.py b/generic_config_updater/field_operation_validators.py index a52a7f5a56..1aae399ade 100644 --- a/generic_config_updater/field_operation_validators.py +++ b/generic_config_updater/field_operation_validators.py @@ -5,7 +5,8 @@ import subprocess from sonic_py_common import device_info from .gu_common import GenericConfigUpdaterError - +from swsscommon import swsscommon +from utilities_common.constants import DEFAULT_SUPPORTED_FECS_LIST SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) GCU_TABLE_MOD_CONF_FILE = f"{SCRIPT_DIR}/gcu_field_operation_validators.conf.json" @@ -75,7 +76,7 @@ def rdma_config_update_validator(patch_element): path = patch_element["path"] table = jsonpointer.JsonPointer(path).parts[0] - # Helper function to return relevant cleaned paths, consdiers case where the jsonpatch value is a dict + # Helper function to return relevant cleaned paths, considers case where the jsonpatch value is a dict # For paths like /PFC_WD/Ethernet112/action, remove Ethernet112 from the path so that we can clearly determine the relevant field (i.e. action, not Ethernet112) def _get_fields_in_patch(): cleaned_fields = [] @@ -130,3 +131,75 @@ def _get_fields_in_patch(): return False return True + + +def read_statedb_entry(table, key, field): + state_db = swsscommon.DBConnector("STATE_DB", 0) + tbl = swsscommon.Table(state_db, table) + return tbl.hget(key, field)[1] + + +def port_config_update_validator(patch_element): + + def _validate_field(field, port, value): + if field == "fec": + supported_fecs_str = read_statedb_entry("PORT_TABLE", port, "supported_fecs") + if supported_fecs_str: + if supported_fecs_str != 'N/A': + supported_fecs_list = [element.strip() for element in supported_fecs_str.split(',')] + else: + supported_fecs_list = [] + else: + supported_fecs_list = DEFAULT_SUPPORTED_FECS_LIST + if value.strip() not in supported_fecs_list: + return False + return True + if field == "speed": + supported_speeds_str = read_statedb_entry("PORT_TABLE", port, "supported_speeds") or '' + try: + supported_speeds = [int(s) for s in supported_speeds_str.split(',') if s] + if supported_speeds and int(value) not in supported_speeds: + return False + except ValueError: + return False + return True + return False + + def _parse_port_from_path(path): + match = re.search(r"Ethernet\d+", path) + if match: + port = match.group(0) + return port + return None + + if patch_element["op"] == "remove": + return True + + # for PORT speed and fec configs, need to ensure value is allowed based on StateDB + patch_element_str = json.dumps(patch_element) + path = patch_element["path"] + value = patch_element.get("value") + fields = ['fec', 'speed'] + for field in fields: + if field in patch_element_str: + if path.endswith(field): + port = _parse_port_from_path(path) + if not _validate_field(field, port, value): + return False + elif isinstance(value, dict): + if field in value.keys(): + port = _parse_port_from_path(path) + value = value[field] + if not _validate_field(field, port, value): + return False + else: + for port_name, port_info in value.items(): + if isinstance(port_info, dict): + port = port_name + if field in port_info.keys(): + value = port_info[field] + if not _validate_field(field, port, value): + return False + else: + continue + return True diff --git a/generic_config_updater/gcu_field_operation_validators.conf.json b/generic_config_updater/gcu_field_operation_validators.conf.json index 7f7d1b9b02..f447b0d851 100644 --- a/generic_config_updater/gcu_field_operation_validators.conf.json +++ b/generic_config_updater/gcu_field_operation_validators.conf.json @@ -152,6 +152,9 @@ } } } + }, + "PORT": { + "field_operation_validators": [ "generic_config_updater.field_operation_validators.port_config_update_validator" ] } } } diff --git a/scripts/db_migrator.py b/scripts/db_migrator.py index 09a6561837..abe47775f5 100755 --- a/scripts/db_migrator.py +++ b/scripts/db_migrator.py @@ -37,7 +37,14 @@ class DBMigrator(): def __init__(self, namespace, socket=None): """ - Version string format: + Version string format (202305 and above): + version__ + branch: master, 202311, 202305, etc. + build: sequentially increase with leading 0 to make it 2 digits. + because the minor number has been removed to make it different + from the old format, adding a leading 0 to make sure that we + have double digit version number spaces. + Version string format (before 202305): version___ major: starting from 1, sequentially incrementing in master branch. @@ -47,7 +54,7 @@ def __init__(self, namespace, socket=None): none-zero values. build: sequentially increase within a minor version domain. """ - self.CURRENT_VERSION = 'version_4_0_3' + self.CURRENT_VERSION = 'version_202305_01' self.TABLE_NAME = 'VERSIONS' self.TABLE_KEY = 'DATABASE' @@ -1008,9 +1015,19 @@ def version_4_0_2(self): def version_4_0_3(self): """ Version 4_0_3. - This is the latest version for master branch + This is the latest version for 202211 branch """ log.log_info('Handling version_4_0_3') + + self.set_version('version_202305_01') + return 'version_202305_01' + + def version_202305_01(self): + """ + Version 202305_01. + This is current last erversion for 202305 branch + """ + log.log_info('Handling version_202305_01') return None def get_version(self): diff --git a/scripts/portconfig b/scripts/portconfig index becc203a90..acdebcc236 100755 --- a/scripts/portconfig +++ b/scripts/portconfig @@ -30,6 +30,8 @@ import sys import decimal import argparse +from utilities_common.constants import DEFAULT_SUPPORTED_FECS_LIST + # mock the redis for unit test purposes # try: if os.environ["UTILITIES_UNIT_TESTING"] == "1" or os.environ["UTILITIES_UNIT_TESTING"] == "2": @@ -276,7 +278,7 @@ class portconfig(object): else: supported_fecs_list = [] else: - supported_fecs_list = ["rs", "fc", "none"] + supported_fecs_list = DEFAULT_SUPPORTED_FECS_LIST return supported_fecs_list diff --git a/scripts/route_check.py b/scripts/route_check.py index 8af8393f46..c0b1be0f55 100755 --- a/scripts/route_check.py +++ b/scripts/route_check.py @@ -110,11 +110,12 @@ def set_level(lvl, log_to_syslog): report_level = syslog.LOG_DEBUG -def print_message(lvl, *args): +def print_message(lvl, *args, write_to_stdout=True): """ print and log the message for given level. :param lvl: Log level for this message as ERR/INFO/DEBUG :param args: message as list of strings or convertible to string + :param write_to_stdout: print the message to stdout if set to true :return None """ msg = "" @@ -125,7 +126,8 @@ def print_message(lvl, *args): break msg += str(arg)[0:rem_len] - print(msg) + if write_to_stdout: + print(msg) if write_to_syslog: syslog.syslog(lvl, msg) diff --git a/tests/db_migrator_test.py b/tests/db_migrator_test.py index d23c541538..15b24d0d53 100644 --- a/tests/db_migrator_test.py +++ b/tests/db_migrator_test.py @@ -25,6 +25,36 @@ def get_sonic_version_info_mlnx(): return {'asic_type': 'mellanox'} +def version_greater_than(v1, v2): + # Return True when v1 is later than v2. Otherwise return False. + if 'master' in v1: + if 'master' in v2: + # both are master versions, directly compare. + return v1 > v2 + + # v1 is master verson and v2 is not, v1 is higher + return True + + if 'master' in v2: + # v2 is master version and v1 is not. + return False + + s1 = v1.split('_') + s2 = v2.split('_') + if len(s1) == 3: + # new format version__ + if len(s2) == 3: + # Both are new format version string + return v1 > v2 + return True + + if len(s2) == 3: + # v2 is new format and v1 is old format. + return False + + # Both are old format version_a_b_c + return v1 > v2 + def advance_version_for_expected_database(migrated_db, expected_db, last_interested_version): # In case there are new db versions greater than the latest one that mellanox buffer migrator is interested, @@ -32,11 +62,41 @@ def advance_version_for_expected_database(migrated_db, expected_db, last_interes expected_dbversion = expected_db.get_entry('VERSIONS', 'DATABASE') dbmgtr_dbversion = migrated_db.get_entry('VERSIONS', 'DATABASE') if expected_dbversion and dbmgtr_dbversion: - if expected_dbversion['VERSION'] == last_interested_version and dbmgtr_dbversion['VERSION'] > expected_dbversion['VERSION']: + if expected_dbversion['VERSION'] == last_interested_version and version_greater_than(dbmgtr_dbversion['VERSION'], expected_dbversion['VERSION']): expected_dbversion['VERSION'] = dbmgtr_dbversion['VERSION'] expected_db.set_entry('VERSIONS', 'DATABASE', expected_dbversion) +class TestVersionComparison(object): + @classmethod + def setup_class(cls): + cls.version_comp_list = [ + # Old format v.s old format + { 'v1' : 'version_1_0_1', 'v2' : 'version_1_0_2', 'result' : False }, + { 'v1' : 'version_1_0_2', 'v2' : 'version_1_0_1', 'result' : True }, + { 'v1' : 'version_1_0_1', 'v2' : 'version_2_0_1', 'result' : False }, + { 'v1' : 'version_2_0_1', 'v2' : 'version_1_0_1', 'result' : True }, + # New format v.s old format + { 'v1' : 'version_1_0_1', 'v2' : 'version_202311_01', 'result' : False }, + { 'v1' : 'version_202311_01', 'v2' : 'version_1_0_1', 'result' : True }, + { 'v1' : 'version_1_0_1', 'v2' : 'version_master_01', 'result' : False }, + { 'v1' : 'version_master_01', 'v2' : 'version_1_0_1', 'result' : True }, + # New format v.s new format + { 'v1' : 'version_202311_01', 'v2' : 'version_202311_02', 'result' : False }, + { 'v1' : 'version_202311_02', 'v2' : 'version_202311_01', 'result' : True }, + { 'v1' : 'version_202305_01', 'v2' : 'version_202311_01', 'result' : False }, + { 'v1' : 'version_202311_01', 'v2' : 'version_202305_01', 'result' : True }, + { 'v1' : 'version_202311_01', 'v2' : 'version_master_01', 'result' : False }, + { 'v1' : 'version_master_01', 'v2' : 'version_202311_01', 'result' : True }, + { 'v1' : 'version_master_01', 'v2' : 'version_master_02', 'result' : False }, + { 'v1' : 'version_master_02', 'v2' : 'version_master_01', 'result' : True }, + ] + + def test_version_comparison(self): + for rec in self.version_comp_list: + assert version_greater_than(rec['v1'], rec['v2']) == rec['result'], 'test failed: {}'.format(rec) + + class TestMellanoxBufferMigrator(object): @classmethod def setup_class(cls): @@ -671,5 +731,6 @@ def test_fast_reboot_upgrade_to_4_0_3(self): dbmgtr = db_migrator.DBMigrator(None) dbmgtr.migrate() expected_db = self.mock_dedicated_config_db(db_after_migrate) + advance_version_for_expected_database(dbmgtr.configDB, expected_db.cfgdb, 'version_4_0_3') assert not self.check_config_db(dbmgtr.configDB, expected_db.cfgdb) - assert dbmgtr.CURRENT_VERSION == expected_db.cfgdb.get_entry('VERSIONS', 'DATABASE')['VERSION'] + assert dbmgtr.CURRENT_VERSION == expected_db.cfgdb.get_entry('VERSIONS', 'DATABASE')['VERSION'], '{} {}'.format(dbmgtr.CURRENT_VERSION, dbmgtr.get_version()) diff --git a/tests/generic_config_updater/field_operation_validator_test.py b/tests/generic_config_updater/field_operation_validator_test.py index 08a56dcf7c..1bc396740c 100644 --- a/tests/generic_config_updater/field_operation_validator_test.py +++ b/tests/generic_config_updater/field_operation_validator_test.py @@ -14,6 +14,85 @@ class TestValidateFieldOperation(unittest.TestCase): + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="")) + def test_port_config_update_validator_valid_speed_no_state_db(self): + patch_element = {"path": "/PORT/Ethernet3", "op": "add", "value": {"speed": "234"}} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == True + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="40000,30000")) + def test_port_config_update_validator_invalid_speed_existing_state_db(self): + patch_element = {"path": "/PORT/Ethernet3", "op": "add", "value": {"speed": "xyz"}} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == False + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="123,234")) + def test_port_config_update_validator_valid_speed_existing_state_db(self): + patch_element = {"path": "/PORT/Ethernet3", "op": "add", "value": {"speed": "234"}} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == True + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="123,234")) + def test_port_config_update_validator_valid_speed_existing_state_db(self): + patch_element = {"path": "/PORT/Ethernet3/speed", "op": "add", "value": "234"} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == True + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="123,234")) + def test_port_config_update_validator_invalid_speed_existing_state_db(self): + patch_element = {"path": "/PORT/Ethernet3/speed", "op": "add", "value": "235"} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == False + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="123,234")) + def test_port_config_update_validator_invalid_speed_existing_state_db_nested(self): + patch_element = {"path": "/PORT", "op": "add", "value": {"Ethernet3": {"alias": "Eth0", "speed": "235"}}} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == False + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="123,234")) + def test_port_config_update_validator_valid_speed_existing_state_db_nested(self): + patch_element = {"path": "/PORT", "op": "add", "value": {"Ethernet3": {"alias": "Eth0", "speed": "234"}, "Ethernet4": {"alias": "Eth4", "speed": "234"}}} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == True + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="123,234")) + def test_port_config_update_validator_invalid_speed_existing_state_db_nested_2(self): + patch_element = {"path": "/PORT", "op": "add", "value": {"Ethernet3": {"alias": "Eth0", "speed": "234"}, "Ethernet4": {"alias": "Eth4", "speed": "236"}}} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == False + + def test_port_config_update_validator_remove(self): + patch_element = {"path": "/PORT/Ethernet3", "op": "remove"} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == True + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="rs, fc")) + def test_port_config_update_validator_invalid_fec_existing_state_db(self): + patch_element = {"path": "/PORT/Ethernet3/fec", "op": "add", "value": "asf"} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == False + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="rs, fc")) + def test_port_config_update_validator_invalid_fec_existing_state_db_nested(self): + patch_element = {"path": "/PORT", "op": "add", "value": {"Ethernet3": {"alias": "Eth0", "fec": "none"}, "Ethernet4": {"alias": "Eth4", "fec": "fs"}}} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == False + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="rs, fc")) + def test_port_config_update_validator_valid_fec_existing_state_db_nested(self): + patch_element = {"path": "/PORT", "op": "add", "value": {"Ethernet3": {"alias": "Eth0", "fec": "fc"}}} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == True + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="rs, fc")) + def test_port_config_update_validator_valid_fec_existing_state_db_nested_2(self): + patch_element = {"path": "/PORT", "op": "add", "value": {"Ethernet3": {"alias": "Eth0", "fec": "rs"}, "Ethernet4": {"alias": "Eth4", "fec": "fc"}}} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == True + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="rs, fc")) + def test_port_config_update_validator_valid_fec_existing_state_db(self): + patch_element = {"path": "/PORT/Ethernet3/fec", "op": "add", "value": "rs"} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == True + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="")) + def test_port_config_update_validator_valid_fec_no_state_db(self): + patch_element = {"path": "/PORT/Ethernet3", "op": "add", "value": {"fec": "rs"}} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == True + + @patch("generic_config_updater.field_operation_validators.read_statedb_entry", mock.Mock(return_value="")) + def test_port_config_update_validator_invalid_fec_no_state_db(self): + patch_element = {"path": "/PORT/Ethernet3/fec", "op": "add", "value": "rsf"} + assert generic_config_updater.field_operation_validators.port_config_update_validator(patch_element) == False + @patch("generic_config_updater.field_operation_validators.get_asic_name", mock.Mock(return_value="unknown")) def test_rdma_config_update_validator_unknown_asic(self): patch_element = {"path": "/PFC_WD/Ethernet4/restoration_time", "op": "replace", "value": "234234"} diff --git a/tests/route_check_test.py b/tests/route_check_test.py index 85e6a64a95..fe6b5bd2a8 100644 --- a/tests/route_check_test.py +++ b/tests/route_check_test.py @@ -1,4 +1,5 @@ import copy +from io import StringIO import json import os import logging @@ -7,7 +8,7 @@ import time from sonic_py_common import device_info from unittest.mock import MagicMock, patch -from tests.route_check_test_data import APPL_DB, ARGS, ASIC_DB, CONFIG_DB, DEFAULT_CONFIG_DB, DESCR, OP_DEL, OP_SET, PRE, RESULT, RET, TEST_DATA, UPD +from tests.route_check_test_data import APPL_DB, ARGS, ASIC_DB, CONFIG_DB, DEFAULT_CONFIG_DB, APPL_STATE_DB, DESCR, OP_DEL, OP_SET, PRE, RESULT, RET, TEST_DATA, UPD import pytest @@ -89,7 +90,7 @@ def hget(self, key, field): return True, ret -db_conns = {"APPL_DB": APPL_DB, "ASIC_DB": ASIC_DB} +db_conns = {"APPL_DB": APPL_DB, "ASIC_DB": ASIC_DB, "APPL_STATE_DB": APPL_STATE_DB } def conn_side_effect(arg, _): return db_conns[arg] @@ -181,7 +182,7 @@ def pop(self): print("state={} k={} op={} v={}".format(self.state, k, op, str(v))) return (k, op, v) - + def getDbConnector(self): return self.dbconn diff --git a/tests/route_check_test_data.py b/tests/route_check_test_data.py index 0f0f74bfe0..3fb6e721a6 100644 --- a/tests/route_check_test_data.py +++ b/tests/route_check_test_data.py @@ -4,6 +4,7 @@ APPL_DB = 0 ASIC_DB = 1 CONFIG_DB = 4 +APPL_STATE_DB = 14 PRE = "pre-value" UPD = "update" RESULT = "res" diff --git a/tests/syslog_test.py b/tests/syslog_test.py index 354100a508..44915b6d36 100644 --- a/tests/syslog_test.py +++ b/tests/syslog_test.py @@ -399,3 +399,89 @@ def test_show_syslog_rate_limit_container_negative(self, subcommand): logger.debug("\n" + result.output) logger.debug(result.exit_code) assert result.exit_code != SUCCESS + + @mock.patch('config.syslog.clicommon.run_command') + def test_enable_syslog_rate_limit_feature(self, mock_run): + db = Db() + db.cfgdb.set_entry(FEATURE_TABLE, 'bgp', {SUPPORT_RATE_LIMIT: 'true', + 'state': 'enabled'}) + + runner = CliRunner() + + mock_run.return_value = ('no such process', 0) + result = runner.invoke( + config.config.commands["syslog"].commands["rate-limit-feature"].commands["enable"], obj=db + ) + assert result.exit_code == SUCCESS + + # container not run + mock_run.return_value = ('', 0) + result = runner.invoke( + config.config.commands["syslog"].commands["rate-limit-feature"].commands["enable"], obj=db + ) + assert result.exit_code == SUCCESS + + # process already running + mock_run.return_value = ('something', 0) + result = runner.invoke( + config.config.commands["syslog"].commands["rate-limit-feature"].commands["enable"], obj=db + ) + assert result.exit_code == SUCCESS + + # one command fail + def side_effect(*args, **kwargs): + side_effect.call_count += 1 + if side_effect.call_count <= 2: + return 'no such process', 0 + else: + return '', -1 + side_effect.call_count = 0 + mock_run.side_effect = side_effect + result = runner.invoke( + config.config.commands["syslog"].commands["rate-limit-feature"].commands["enable"], obj=db + ) + assert result.exit_code == SUCCESS + + + @mock.patch('config.syslog.clicommon.run_command') + def test_disable_syslog_rate_limit_feature(self, mock_run): + db = Db() + db.cfgdb.set_entry(FEATURE_TABLE, 'bgp', {SUPPORT_RATE_LIMIT: 'true', + 'state': 'enabled'}) + + runner = CliRunner() + + mock_run.return_value = ('something', 0) + result = runner.invoke( + config.config.commands["syslog"].commands["rate-limit-feature"].commands["disable"], obj=db + ) + assert result.exit_code == SUCCESS + + # container not run + mock_run.return_value = ('', 0) + result = runner.invoke( + config.config.commands["syslog"].commands["rate-limit-feature"].commands["disable"], obj=db + ) + assert result.exit_code == SUCCESS + + # process already stopped + mock_run.return_value = ('no such process', 0) + result = runner.invoke( + config.config.commands["syslog"].commands["rate-limit-feature"].commands["disable"], obj=db + ) + assert result.exit_code == SUCCESS + + # one command fail + def side_effect(*args, **kwargs): + side_effect.call_count += 1 + if side_effect.call_count <= 2: + return 'something', 0 + else: + return '', -1 + side_effect.call_count = 0 + mock_run.side_effect = side_effect + result = runner.invoke( + config.config.commands["syslog"].commands["rate-limit-feature"].commands["disable"], obj=db + ) + assert result.exit_code == SUCCESS + diff --git a/utilities_common/constants.py b/utilities_common/constants.py index 536965d009..f5c157941c 100644 --- a/utilities_common/constants.py +++ b/utilities_common/constants.py @@ -1,6 +1,7 @@ #All the constant used in sonic-utilities DEFAULT_NAMESPACE = '' +DEFAULT_SUPPORTED_FECS_LIST = [ 'rs', 'fc', 'none'] DISPLAY_ALL = 'all' DISPLAY_EXTERNAL = 'frontend' BGP_NEIGH_OBJ = 'BGP_NEIGH'