From fb4a090d3eab28d32012cfd7e649edf5a54b7fbe Mon Sep 17 00:00:00 2001 From: jingwenxie Date: Thu, 7 Mar 2024 10:02:30 +0800 Subject: [PATCH] [config] Add Table hard dependency check (#3159) ADO: 26732148 #### What I did Add YANG hard depdency check for AAA and TACPLUS table #### How I did it Add a special check #### How to verify it Unit test --- config/main.py | 19 +++++++++++++ .../aaa_yang_hard_check.json | 28 +++++++++++++++++++ tests/config_override_test.py | 20 +++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 tests/config_override_input/aaa_yang_hard_check.json diff --git a/config/main.py b/config/main.py index 2e20b5dad6..401359b29c 100644 --- a/config/main.py +++ b/config/main.py @@ -1953,6 +1953,9 @@ def override_config_table(db, input_config_db, dry_run): # Use deepcopy by default to avoid modifying input config updated_config = update_config(current_config, ns_config_input) + # Enable YANG hard dependecy check to exit early if not satisfied + table_hard_dependency_check(updated_config) + yang_enabled = device_info.is_yang_config_validation_enabled(config_db) if yang_enabled: # The ConfigMgmt will load YANG and running @@ -2020,6 +2023,22 @@ def override_config_db(config_db, config_input): click.echo("Overriding completed. No service is restarted.") +def table_hard_dependency_check(config_json): + aaa_table_hard_dependency_check(config_json) + + +def aaa_table_hard_dependency_check(config_json): + AAA_TABLE = config_json.get("AAA", {}) + TACPLUS_TABLE = config_json.get("TACPLUS", {}) + + aaa_authentication_login = AAA_TABLE.get("authentication", {}).get("login", "") + tacacs_enable = "tacacs+" in aaa_authentication_login.split(",") + tacplus_passkey = TACPLUS_TABLE.get("global", {}).get("passkey", "") + if tacacs_enable and len(tacplus_passkey) == 0: + click.secho("Authentication with 'tacacs+' is not allowed when passkey not exits.", fg="magenta") + sys.exit(1) + + # # 'hostname' command # diff --git a/tests/config_override_input/aaa_yang_hard_check.json b/tests/config_override_input/aaa_yang_hard_check.json new file mode 100644 index 0000000000..61794f1ece --- /dev/null +++ b/tests/config_override_input/aaa_yang_hard_check.json @@ -0,0 +1,28 @@ +{ + "running_config": { + "AAA": { + "authentication": { + "login": "tacacs+" + } + }, + "TACPLUS": { + "global": { + "passkey": "" + } + } + }, + "golden_config": { + }, + "expected_config": { + "AAA": { + "authentication": { + "login": "tacacs+" + } + }, + "TACPLUS": { + "global": { + "passkey": "" + } + } + } +} diff --git a/tests/config_override_test.py b/tests/config_override_test.py index ae120c9747..a46be5ef60 100644 --- a/tests/config_override_test.py +++ b/tests/config_override_test.py @@ -18,6 +18,7 @@ FULL_CONFIG_OVERRIDE = os.path.join(DATA_DIR, "full_config_override.json") PORT_CONFIG_OVERRIDE = os.path.join(DATA_DIR, "port_config_override.json") EMPTY_TABLE_REMOVAL = os.path.join(DATA_DIR, "empty_table_removal.json") +AAA_YANG_HARD_CHECK = os.path.join(DATA_DIR, "aaa_yang_hard_check.json") RUNNING_CONFIG_YANG_FAILURE = os.path.join(DATA_DIR, "running_config_yang_failure.json") GOLDEN_INPUT_YANG_FAILURE = os.path.join(DATA_DIR, "golden_input_yang_failure.json") FINAL_CONFIG_YANG_FAILURE = os.path.join(DATA_DIR, "final_config_yang_failure.json") @@ -161,6 +162,25 @@ def test_golden_config_db_empty_table_removal(self): db, config, read_data['running_config'], read_data['golden_config'], read_data['expected_config']) + def test_aaa_yang_hard_depdency_check_failure(self): + """YANG hard depdency must be satisfied""" + db = Db() + with open(AAA_YANG_HARD_CHECK, "r") as f: + read_data = json.load(f) + def read_json_file_side_effect(filename): + return read_data['golden_config'] + + with mock.patch('config.main.read_json_file', + mock.MagicMock(side_effect=read_json_file_side_effect)): + write_init_config_db(db.cfgdb, read_data['running_config']) + + runner = CliRunner() + result = runner.invoke(config.config.commands["override-config-table"], + ['golden_config_db.json'], obj=db) + + assert result.exit_code != 0 + assert "Authentication with 'tacacs+' is not allowed when passkey not exits." in result.output + def check_override_config_table(self, db, config, running_config, golden_config, expected_config): def read_json_file_side_effect(filename):