From 5d5ca7a87d6de111a294f1faa5cacd41d1b46620 Mon Sep 17 00:00:00 2001 From: Xincun Li Date: Mon, 10 Jun 2024 11:35:38 -0700 Subject: [PATCH 01/17] Add Multi ASIC GCU test cases for IDF and LinkCRC. --- tests/generic_config_updater/gu_utils.py | 15 ++ .../test_multiasic_scenarios.py | 208 ++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 tests/generic_config_updater/test_multiasic_scenarios.py diff --git a/tests/generic_config_updater/gu_utils.py b/tests/generic_config_updater/gu_utils.py index 2566015d0a3..04fd70c0d57 100644 --- a/tests/generic_config_updater/gu_utils.py +++ b/tests/generic_config_updater/gu_utils.py @@ -50,6 +50,21 @@ def apply_patch(duthost, json_data, dest_file): return output +def replace(duthost, replace_config_file): + """Run replace with given config file on target duthost + + Args: + duthost: Device Under Test (DUT) + replace_config_file: Destination file on duthost + """ + cmds = 'config replace {}'.format(replace_config_file) + + logger.info("Commands: {}".format(cmds)) + output = duthost.shell(cmds, module_ignore_errors=True) + + return output + + def expect_op_success(duthost, output): """Expected success from apply-patch output """ diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py new file mode 100644 index 00000000000..c53d952f340 --- /dev/null +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -0,0 +1,208 @@ +import logging +import pytest +import re + +from tests.common.helpers.assertions import pytest_assert +from tests.generic_config_updater.gu_utils import DEFAULT_CHECKPOINT_NAME, apply_patch, replace +from tests.generic_config_updater.gu_utils import generate_tmpfile, delete_tmpfile +from tests.generic_config_updater.gu_utils import create_checkpoint, delete_checkpoint, rollback_or_reload + +pytestmark = [ + pytest.mark.topology('any'), +] + +logger = logging.getLogger(__name__) + +IDF_ISOLATION = [ + { + "op": "add", + "path": "/asic0/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", + "value": "isolated_no_export" + }, + { + "op": "add", + "path": "/asic1/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", + "value": "isolated_withdraw_all" + }, +] + +IDF_UNISOLATION = [ + { + "op": "add", + "path": "/asic0/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", + "value": "unisolated" + }, + { + "op": "add", + "path": "/asic1/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", + "value": "unisolated" + }, +] + +LINK_CRC_MITIGATION_REMOVE_TEMPLATE = '[{"op": "remove", "path": "/asic0/PORTCHANNEL_MEMBER/{}|{}"}]' +LINK_CRC_MITIGATION_ADD_TEMPLATE = '[{{"op": "add", "path": "/asic0/PORTCHANNEL_MEMBER/{}|{}", "value": {}}}]' + + +def extract_up_interface(output): + # Updated regex pattern to match both (U) and (S) status + pattern = re.compile(r"^\s*(\d+)\s+(PortChannel\d+)\s+LACP\(\w+\)\(Up\)\s+(Ethernet\d+)\([US]\)", re.MULTILINE) + match = pattern.search(output) + if match: + return match.group(2), match.group(3) + else: + return None, None + + +@pytest.fixture(autouse=True) +def setup_env(duthosts, rand_one_dut_hostname): + """ + Setup/teardown fixture for each multi asic test. + rollback to check if it goes back to starting config + + Args: + duthosts: list of DUTs. + rand_selected_dut: The fixture returns a randomly selected DuT. + """ + duthost = duthosts[rand_one_dut_hostname] + + create_checkpoint(duthost) + + yield + + try: + logger.info("Rolled back to original checkpoint") + rollback_or_reload(duthost) + finally: + delete_checkpoint(duthost) + + +def test_check_empty_apply_patch(duthost): + json_patch = [] + tmpfile = generate_tmpfile(duthost) + + try: + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + finally: + delete_tmpfile(duthost, tmpfile) + + if output['rc'] or "Patch applied successfully" not in output['stdout']: + logger.info("Patching process broken, the error output is {}").format(output['stdout']) + pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) + + +def test_check_idf_isolation_apply_patch(duthost): + json_patch = IDF_ISOLATION + tmpfile = generate_tmpfile(duthost) + + try: + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + + if output['rc'] or "Patch applied successfully" not in output['stdout']: + logger.info(f"Patching process broken, the error output is {output['stdout']}") + pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) + + cmds = 'sonic-db-cli -n asic0 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" "idf_isolation_state"' + expected_value = "isolated_no_export" + redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] + pytest_assert(redis_value == expected_value, "Config IDF ISOLATION failed") + + cmds = 'sonic-db-cli -n asic1 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" "idf_isolation_state"' + expected_value = "isolated_withdraw_all" + redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] + pytest_assert(redis_value == expected_value, "Config IDF ISOLATION failed") + finally: + delete_tmpfile(duthost, tmpfile) + + +def test_check_idf_unisolation_apply_patch(duthost): + json_patch = IDF_UNISOLATION + tmpfile = generate_tmpfile(duthost) + + try: + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + + if output['rc'] or "Patch applied successfully" not in output['stdout']: + logger.info(f"Patching process broken, the error output is {output['stdout']}") + pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) + + cmds = ["sonic-db-cli", "-n", "asic0", "CONFIG_DB", + "hget", "\"BGP_DEVICE_GLOBAL|STATE\"" "\"idf_isolation_state\""] + expected_value = "unisolated" + redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] + pytest_assert(redis_value == expected_value, "Config IDF ISOLATION failed") + + cmds = 'sonic-db-cli -n asic1 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" "idf_isolation_state"' + expected_value = "unisolated" + redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] + pytest_assert(redis_value == expected_value, "Config IDF ISOLATION failed") + finally: + delete_tmpfile(duthost, tmpfile) + + +def test_check_link_crc_mitigation_remove_and_add_apply_patch(duthost): + tmpfile = generate_tmpfile(duthost) + + try: + result = duthost.shell(["show", "interfaces", "portchannel", "-n", "asic0"]) + portchannel, port = extract_up_interface(result) + + # Precheck keys existing + cmds = ["sonic-db-cli", "-n", "asic0", "CONFIG_DB", "keys", f"\"PORTCHANNEL_MEMBER|{portchannel}|{port}\""] + expected_value = f"PORTCHANNEL_MEMBER|{portchannel}|{port}" + redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] + pytest_assert(redis_value == expected_value, "Config Link CRC Mitigation add action failed.") + + json_patch = LINK_CRC_MITIGATION_REMOVE_TEMPLATE.format(portchannel, port) + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + + if output['rc'] or "Patch applied successfully" not in output['stdout']: + logger.info(f"Patching process broken, the error output is {output['stdout']}") + pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) + + cmds = ["sonic-db-cli", "-n", "asic0", "CONFIG_DB", "keys", f"\"PORTCHANNEL_MEMBER|{portchannel}|{port}\""] + expected_value = "" + redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] + pytest_assert(redis_value.strip() == expected_value, "Config Link CRC Mitigation remove action failed.") + + json_patch = LINK_CRC_MITIGATION_ADD_TEMPLATE.format(portchannel, port, "{}") + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + + if output['rc'] or "Patch applied successfully" not in output['stdout']: + logger.info(f"Patching process broken, the error output is {output['stdout']}") + pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) + + cmds = ["sonic-db-cli", "-n", "asic0", "CONFIG_DB", "keys", f"\"PORTCHANNEL_MEMBER|{portchannel}|{port}\""] + expected_value = f"PORTCHANNEL_MEMBER|{portchannel}|{port}" + redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] + pytest_assert(redis_value == expected_value, "Config Link CRC Mitigation add action failed.") + finally: + delete_tmpfile(duthost, tmpfile) + + +def check_apply_patch_negative_case(duthost): + json_patch = '[{"op": "add"}]' + tmpfile = generate_tmpfile(duthost) + + try: + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + finally: + delete_tmpfile(duthost, tmpfile) + + if output['rc'] or "Failed to apply patch" not in output['stdout']: + logger.info("Negative patching process broken, the error output is {}").format(output['stdout']) + pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) + + +def check_replace(duthost): + tmpfile = generate_tmpfile(duthost) + + duthost.copy(src=f"/etc/sonic/checkpoints/{DEFAULT_CHECKPOINT_NAME}.cp.json", dest=tmpfile) + + try: + output = replace(duthost, replace_config_file=tmpfile) + finally: + delete_tmpfile(duthost, tmpfile) + + if output['rc'] or "Config replaced successfully" not in output['stdout']: + logger.info("Replacing process broken, the error output is {}").format(output['stdout']) + pytest_assert(False, "Replacing process broken, the error output is {}").format(output['stdout']) From 7b802af088418e746b44777f101cce7deba9e4be Mon Sep 17 00:00:00 2001 From: Xincun Li Date: Mon, 10 Jun 2024 15:15:10 -0700 Subject: [PATCH 02/17] Add test into pr script --- .azure-pipelines/pr_test_scripts.yaml | 1 + tests/generic_config_updater/test_multiasic_scenarios.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.azure-pipelines/pr_test_scripts.yaml b/.azure-pipelines/pr_test_scripts.yaml index e8a25798dc7..fd5a544ad21 100644 --- a/.azure-pipelines/pr_test_scripts.yaml +++ b/.azure-pipelines/pr_test_scripts.yaml @@ -243,6 +243,7 @@ multi-asic-t1-lag: t2: - test_vs_chassis_setup.py - voq/test_voq_init.py + - generic_config_updater/test_multiasic_scenarios.py wan-pub: - system_health/test_system_status.py diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py index c53d952f340..d2481cd4e56 100644 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -8,7 +8,7 @@ from tests.generic_config_updater.gu_utils import create_checkpoint, delete_checkpoint, rollback_or_reload pytestmark = [ - pytest.mark.topology('any'), + pytest.mark.topology('t2'), ] logger = logging.getLogger(__name__) From 7b2079b5869689b22896683591b494ce54f26f2e Mon Sep 17 00:00:00 2001 From: Xincun Li Date: Tue, 11 Jun 2024 16:15:11 -0700 Subject: [PATCH 03/17] Modify the test case to multiasic t1 --- .azure-pipelines/pr_test_scripts.yaml | 4 ++-- tests/generic_config_updater/test_multiasic_scenarios.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.azure-pipelines/pr_test_scripts.yaml b/.azure-pipelines/pr_test_scripts.yaml index fd5a544ad21..a980381e73c 100644 --- a/.azure-pipelines/pr_test_scripts.yaml +++ b/.azure-pipelines/pr_test_scripts.yaml @@ -239,12 +239,12 @@ multi-asic-t1-lag: - process_monitoring/test_critical_process_monitoring.py - container_checker/test_container_checker.py - http/test_http_copy.py + - generic_config_updater/test_multiasic_scenarios.py + t2: - test_vs_chassis_setup.py - voq/test_voq_init.py - - generic_config_updater/test_multiasic_scenarios.py - wan-pub: - system_health/test_system_status.py - snmp/test_snmp_cpu.py diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py index d2481cd4e56..c53d952f340 100644 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -8,7 +8,7 @@ from tests.generic_config_updater.gu_utils import create_checkpoint, delete_checkpoint, rollback_or_reload pytestmark = [ - pytest.mark.topology('t2'), + pytest.mark.topology('any'), ] logger = logging.getLogger(__name__) From a649abf35ac1e37c8f3de1b3cbcdf2c1881d3121 Mon Sep 17 00:00:00 2001 From: Xincun Li <147451452+xincunli-sonic@users.noreply.github.com> Date: Fri, 9 Aug 2024 16:52:28 -0700 Subject: [PATCH 04/17] Update test_multiasic_scenarios.py --- .../test_multiasic_scenarios.py | 63 +++++++------------ 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py index c53d952f340..b52bcfec50e 100644 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -1,3 +1,4 @@ +import json import logging import pytest import re @@ -39,7 +40,7 @@ }, ] -LINK_CRC_MITIGATION_REMOVE_TEMPLATE = '[{"op": "remove", "path": "/asic0/PORTCHANNEL_MEMBER/{}|{}"}]' +LINK_CRC_MITIGATION_REMOVE_TEMPLATE = '[{{"op": "remove", "path": "/asic0/PORTCHANNEL_MEMBER/{}|{}"}}]' LINK_CRC_MITIGATION_ADD_TEMPLATE = '[{{"op": "add", "path": "/asic0/PORTCHANNEL_MEMBER/{}|{}", "value": {}}}]' @@ -58,7 +59,6 @@ def setup_env(duthosts, rand_one_dut_hostname): """ Setup/teardown fixture for each multi asic test. rollback to check if it goes back to starting config - Args: duthosts: list of DUTs. rand_selected_dut: The fixture returns a randomly selected DuT. @@ -98,10 +98,10 @@ def test_check_idf_isolation_apply_patch(duthost): output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) if output['rc'] or "Patch applied successfully" not in output['stdout']: - logger.info(f"Patching process broken, the error output is {output['stdout']}") + logger.info("Patching process broken, the error output is {}".format(output['stdout'])) pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) - cmds = 'sonic-db-cli -n asic0 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" "idf_isolation_state"' + cmds = 'sonic-db-cli -n asic0 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" idf_isolation_state' expected_value = "isolated_no_export" redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] pytest_assert(redis_value == expected_value, "Config IDF ISOLATION failed") @@ -122,16 +122,15 @@ def test_check_idf_unisolation_apply_patch(duthost): output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) if output['rc'] or "Patch applied successfully" not in output['stdout']: - logger.info(f"Patching process broken, the error output is {output['stdout']}") + logger.info("Patching process broken, the error output is {}".format(output['stdout'])) pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) - - cmds = ["sonic-db-cli", "-n", "asic0", "CONFIG_DB", - "hget", "\"BGP_DEVICE_GLOBAL|STATE\"" "\"idf_isolation_state\""] + + cmds = 'sonic-db-cli -n asic0 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" idf_isolation_state' expected_value = "unisolated" redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] pytest_assert(redis_value == expected_value, "Config IDF ISOLATION failed") - cmds = 'sonic-db-cli -n asic1 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" "idf_isolation_state"' + cmds = 'sonic-db-cli -n asic1 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" idf_isolation_state' expected_value = "unisolated" redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] pytest_assert(redis_value == expected_value, "Config IDF ISOLATION failed") @@ -143,66 +142,50 @@ def test_check_link_crc_mitigation_remove_and_add_apply_patch(duthost): tmpfile = generate_tmpfile(duthost) try: - result = duthost.shell(["show", "interfaces", "portchannel", "-n", "asic0"]) + result = duthost.shell("show interfaces portchannel -n asic0", module_ignore_errors=False)['stdout'] portchannel, port = extract_up_interface(result) # Precheck keys existing - cmds = ["sonic-db-cli", "-n", "asic0", "CONFIG_DB", "keys", f"\"PORTCHANNEL_MEMBER|{portchannel}|{port}\""] - expected_value = f"PORTCHANNEL_MEMBER|{portchannel}|{port}" + cmds = 'sonic-db-cli -n asic0 CONFIG_DB keys "PORTCHANNEL_MEMBER|{}|{}"'.format(portchannel, port) + expected_value = "PORTCHANNEL_MEMBER|{}|{}".format(portchannel, port) redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] pytest_assert(redis_value == expected_value, "Config Link CRC Mitigation add action failed.") json_patch = LINK_CRC_MITIGATION_REMOVE_TEMPLATE.format(portchannel, port) - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile) if output['rc'] or "Patch applied successfully" not in output['stdout']: - logger.info(f"Patching process broken, the error output is {output['stdout']}") + logger.info("Patching process broken, the error output is {}".format(output['stdout'])) pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) - cmds = ["sonic-db-cli", "-n", "asic0", "CONFIG_DB", "keys", f"\"PORTCHANNEL_MEMBER|{portchannel}|{port}\""] + cmds = 'sonic-db-cli -n asic0 CONFIG_DB keys "PORTCHANNEL_MEMBER|{}|{}"'.format(portchannel, port) expected_value = "" redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] pytest_assert(redis_value.strip() == expected_value, "Config Link CRC Mitigation remove action failed.") json_patch = LINK_CRC_MITIGATION_ADD_TEMPLATE.format(portchannel, port, "{}") - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile) if output['rc'] or "Patch applied successfully" not in output['stdout']: - logger.info(f"Patching process broken, the error output is {output['stdout']}") + logger.info("Patching process broken, the error output is {}".format(output['stdout'])) pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) - cmds = ["sonic-db-cli", "-n", "asic0", "CONFIG_DB", "keys", f"\"PORTCHANNEL_MEMBER|{portchannel}|{port}\""] - expected_value = f"PORTCHANNEL_MEMBER|{portchannel}|{port}" + cmds = 'sonic-db-cli -n asic0 CONFIG_DB keys "PORTCHANNEL_MEMBER|{}|{}"'.format(portchannel, port) + expected_value = "PORTCHANNEL_MEMBER|{}|{}".format(portchannel, port) redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] pytest_assert(redis_value == expected_value, "Config Link CRC Mitigation add action failed.") finally: delete_tmpfile(duthost, tmpfile) -def check_apply_patch_negative_case(duthost): - json_patch = '[{"op": "add"}]' - tmpfile = generate_tmpfile(duthost) - - try: - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) - finally: - delete_tmpfile(duthost, tmpfile) - - if output['rc'] or "Failed to apply patch" not in output['stdout']: - logger.info("Negative patching process broken, the error output is {}").format(output['stdout']) - pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) - - -def check_replace(duthost): +def test_check_apply_patch_negative_case(duthost): + json_patch = '[{"op": "replace", "path": "/x"}]' tmpfile = generate_tmpfile(duthost) - duthost.copy(src=f"/etc/sonic/checkpoints/{DEFAULT_CHECKPOINT_NAME}.cp.json", dest=tmpfile) - try: - output = replace(duthost, replace_config_file=tmpfile) + output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile) finally: delete_tmpfile(duthost, tmpfile) - if output['rc'] or "Config replaced successfully" not in output['stdout']: - logger.info("Replacing process broken, the error output is {}").format(output['stdout']) - pytest_assert(False, "Replacing process broken, the error output is {}").format(output['stdout']) + pytest_assert(output['rc'] != 0 and "Failed to apply patch" in output['stderr'], + "Expected failure did not occur as expected. Output: {}".format(output['stderr'])) From b764882ee2eaf883fdfb905bc2a6d57366931e35 Mon Sep 17 00:00:00 2001 From: Xincun Li <147451452+xincunli-sonic@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:53:59 -0700 Subject: [PATCH 05/17] Update test_multiasic_scenarios.py --- tests/generic_config_updater/test_multiasic_scenarios.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py index b52bcfec50e..ab426756d81 100644 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -4,7 +4,7 @@ import re from tests.common.helpers.assertions import pytest_assert -from tests.generic_config_updater.gu_utils import DEFAULT_CHECKPOINT_NAME, apply_patch, replace +from tests.generic_config_updater.gu_utils import apply_patch from tests.generic_config_updater.gu_utils import generate_tmpfile, delete_tmpfile from tests.generic_config_updater.gu_utils import create_checkpoint, delete_checkpoint, rollback_or_reload @@ -188,4 +188,4 @@ def test_check_apply_patch_negative_case(duthost): delete_tmpfile(duthost, tmpfile) pytest_assert(output['rc'] != 0 and "Failed to apply patch" in output['stderr'], - "Expected failure did not occur as expected. Output: {}".format(output['stderr'])) + "Expected failure did not occur as expected. Output: {}".format(output['stderr'])) From 18519136da6bef38b1cbb501bc74b63d15911cc4 Mon Sep 17 00:00:00 2001 From: Xincun Li <147451452+xincunli-sonic@users.noreply.github.com> Date: Sat, 10 Aug 2024 14:56:54 -0700 Subject: [PATCH 06/17] fix format --- tests/generic_config_updater/test_multiasic_scenarios.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py index ab426756d81..9c47cf145c9 100644 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -188,4 +188,5 @@ def test_check_apply_patch_negative_case(duthost): delete_tmpfile(duthost, tmpfile) pytest_assert(output['rc'] != 0 and "Failed to apply patch" in output['stderr'], - "Expected failure did not occur as expected. Output: {}".format(output['stderr'])) + "Expected failure did not occur as expected. Output: {}".format(output['stderr'])) + From 04a61b8691f16da7db17e3ba0aaca5cb7529bc5b Mon Sep 17 00:00:00 2001 From: Xincun Li <147451452+xincunli-sonic@users.noreply.github.com> Date: Mon, 19 Aug 2024 09:15:34 -0700 Subject: [PATCH 07/17] fix format --- .../test_multiasic_scenarios.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py index 9c47cf145c9..18d4eec8a2b 100644 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -183,10 +183,15 @@ def test_check_apply_patch_negative_case(duthost): tmpfile = generate_tmpfile(duthost) try: - output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile) + output = apply_patch( + duthost, json_data=json.loads(json_patch), dest_file=tmpfile + ) finally: delete_tmpfile(duthost, tmpfile) - pytest_assert(output['rc'] != 0 and "Failed to apply patch" in output['stderr'], - "Expected failure did not occur as expected. Output: {}".format(output['stderr'])) - + pytest_assert( + output["rc"] != 0 and "Failed to apply patch" in output["stderr"], + "Expected failure did not occur as expected. Output: {}".format( + output["stderr"] + ), + ) From 8a98fbf02cd9b29ab33380c207483efca59c1615 Mon Sep 17 00:00:00 2001 From: "Steve Li(Azure)" Date: Mon, 7 Oct 2024 11:15:12 -0700 Subject: [PATCH 08/17] Add PORT table as ignored. --- tests/generic_config_updater/gu_utils.py | 5 +++-- tests/generic_config_updater/test_multiasic_scenarios.py | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/generic_config_updater/gu_utils.py b/tests/generic_config_updater/gu_utils.py index 04fd70c0d57..c8850a81fcc 100644 --- a/tests/generic_config_updater/gu_utils.py +++ b/tests/generic_config_updater/gu_utils.py @@ -32,17 +32,18 @@ def delete_tmpfile(duthost, tmpfile): duthost.file(path=tmpfile, state='absent') -def apply_patch(duthost, json_data, dest_file): +def apply_patch(duthost, json_data, dest_file, ignore_tables=None): """Run apply-patch on target duthost Args: duthost: Device Under Test (DUT) json_data: Source json patch to apply dest_file: Destination file on duthost + ignore_tables: to be ignored tables, "-i table_name" """ duthost.copy(content=json.dumps(json_data, indent=4), dest=dest_file) - cmds = 'config apply-patch {}'.format(dest_file) + cmds = 'config apply-patch {} {}'.format(dest_file, ignore_tables if ignore_tables else "") logger.info("Commands: {}".format(cmds)) output = duthost.shell(cmds, module_ignore_errors=True) diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py index 18d4eec8a2b..accabdb9282 100644 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -95,7 +95,7 @@ def test_check_idf_isolation_apply_patch(duthost): tmpfile = generate_tmpfile(duthost) try: - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile, ignore_tables="-i PORT") if output['rc'] or "Patch applied successfully" not in output['stdout']: logger.info("Patching process broken, the error output is {}".format(output['stdout'])) @@ -119,7 +119,7 @@ def test_check_idf_unisolation_apply_patch(duthost): tmpfile = generate_tmpfile(duthost) try: - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile, ignore_tables="-i PORT") if output['rc'] or "Patch applied successfully" not in output['stdout']: logger.info("Patching process broken, the error output is {}".format(output['stdout'])) @@ -152,7 +152,7 @@ def test_check_link_crc_mitigation_remove_and_add_apply_patch(duthost): pytest_assert(redis_value == expected_value, "Config Link CRC Mitigation add action failed.") json_patch = LINK_CRC_MITIGATION_REMOVE_TEMPLATE.format(portchannel, port) - output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile) + output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile, ignore_tables="-i PORT") if output['rc'] or "Patch applied successfully" not in output['stdout']: logger.info("Patching process broken, the error output is {}".format(output['stdout'])) From a62f89bb0d798c396427db6d1cd9e613e09f98b1 Mon Sep 17 00:00:00 2001 From: "Steve Li(Azure)" Date: Mon, 7 Oct 2024 11:30:52 -0700 Subject: [PATCH 09/17] Merge from master --- tests/generic_config_updater/gu_utils.py | 223 ++++++----------------- 1 file changed, 52 insertions(+), 171 deletions(-) diff --git a/tests/generic_config_updater/gu_utils.py b/tests/generic_config_updater/gu_utils.py index 4497d65a233..377ffff4c2d 100644 --- a/tests/generic_config_updater/gu_utils.py +++ b/tests/generic_config_updater/gu_utils.py @@ -1,23 +1,19 @@ -import os -import logging import json -from tests.common.gu_utils import apply_patch, generate_tmpfile, delete_tmpfile +import logging +import pytest -BASE_DIR = os.path.dirname(os.path.realpath(__file__)) -TEMPLATES_DIR = os.path.join(BASE_DIR, "../generic_config_updater/templates") -TMP_DIR = '/tmp' +from tests.common import config_reload +from tests.common.helpers.assertions import pytest_assert logger = logging.getLogger(__name__) +DEFAULT_CHECKPOINT_NAME = "test" -def format_and_apply_template(duthost, template_name, extra_vars, setup): - dest_path = os.path.join(TMP_DIR, template_name) - - duts_to_apply = [duthost] - outputs = [] - if setup["is_dualtor"]: - duts_to_apply.append(setup["rand_unselected_dut"]) +def generate_tmpfile(duthost): + """Generate temp file + """ + return duthost.shell('mktemp')['stdout'] def apply_patch(duthost, json_data, dest_file, ignore_tables=None): @@ -39,133 +35,29 @@ def apply_patch(duthost, json_data, dest_file, ignore_tables=None): return output -def replace(duthost, replace_config_file): - """Run replace with given config file on target duthost - - Args: - duthost: Device Under Test (DUT) - replace_config_file: Destination file on duthost - """ - cmds = 'config replace {}'.format(replace_config_file) - - logger.info("Commands: {}".format(cmds)) - output = duthost.shell(cmds, module_ignore_errors=True) - - return output - - -def expect_op_success(duthost, output): - """Expected success from apply-patch output - """ - pytest_assert(not output['rc'], "Command is not running successfully") - pytest_assert( - "Patch applied successfully" in output['stdout'], - "Please check if json file is validate" - ) - - -def expect_op_success_and_reset_check(duthost, output, service_name, timeout, interval, delay): - """Add contianer reset check after op success - - Args: - duthost: Device Under Test (DUT) - output: Command couput - service_name: Service to reset - timeout: Maximum time to wait - interval: Poll interval - delay: Delay time +def delete_tmpfile(duthost, tmpfile): + """Delete temp file """ - expect_op_success(duthost, output) - if start_limit_hit(duthost, service_name): - reset_start_limit_hit(duthost, service_name, timeout, interval, delay) - + duthost.file(path=tmpfile, state='absent') -def expect_res_success(duthost, output, expected_content_list, unexpected_content_list): - """Check output success with expected and unexpected content - Args: - duthost: Device Under Test (DUT) - output: Command output - expected_content_list: Expected content from output - unexpected_content_list: Unexpected content from output - """ - for expected_content in expected_content_list: - pytest_assert( - expected_content in output['stdout'], - "{} is expected content".format(expected_content) - ) - - for unexpected_content in unexpected_content_list: - pytest_assert( - unexpected_content not in output['stdout'], - "{} is unexpected content".format(unexpected_content) - ) - - -def expect_op_failure(output): - """Expected failure from apply-patch output - """ - logger.info("return code {}".format(output['rc'])) - pytest_assert( - output['rc'], - "The command should fail with non zero return code" - ) - - -def start_limit_hit(duthost, service_name): - """If start-limit-hit is hit, the service will not start anyway. - - Args: - service_name: Service to reset - """ - service_status = duthost.shell("systemctl status {}.service | grep 'Active'".format(service_name)) - pytest_assert( - not service_status['rc'], - "{} service status cannot be found".format(service_name) - ) - - for line in service_status["stdout_lines"]: - if "start-limit-hit" in line: - return True - - return False - - -def reset_start_limit_hit(duthost, service_name, timeout, interval, delay): - """Reset service if hit start-limit-hit +def create_checkpoint(duthost, cp=DEFAULT_CHECKPOINT_NAME): + """Run checkpoint on target duthost Args: duthost: Device Under Test (DUT) - service_name: Service to reset - timeout: Maximum time to wait - interval: Poll interval - delay: Delay time + cp: checkpoint filename """ - logger.info("Reset service '{}' due to start-limit-hit".format(service_name)) - - service_reset_failed = duthost.shell("systemctl reset-failed {}.service".format(service_name)) - pytest_assert( - not service_reset_failed['rc'], - "{} systemctl reset-failed service fails" - ) - - service_start = duthost.shell("systemctl start {}.service".format(service_name)) - pytest_assert( - not service_start['rc'], - "{} systemctl start service fails" - ) + cmds = 'config checkpoint {}'.format(cp) - if service_name not in CONTAINER_SERVICES_LIST: - return + logger.info("Commands: {}".format(cmds)) + output = duthost.shell(cmds, module_ignore_errors=True) - reset_service = wait_until(timeout, - interval, - delay, - duthost.is_service_fully_started, - service_name) pytest_assert( - reset_service, - "Failed to reset service '{}' due to start-limit-hit".format(service_name) + not output['rc'] + and "Checkpoint created successfully" in output['stdout'] + and verify_checkpoints_exist(duthost, cp), + "Failed to config a checkpoint file: {}".format(cp) ) @@ -174,7 +66,6 @@ def list_checkpoints(duthost): Args: duthost: Device Under Test (DUT) - cp: checkpoint filename """ cmds = 'config list-checkpoints' @@ -196,24 +87,32 @@ def verify_checkpoints_exist(duthost, cp): return '"{}"'.format(cp) in output['stdout'] -def create_checkpoint(duthost, cp=DEFAULT_CHECKPOINT_NAME): - """Run checkpoint on target duthost +def rollback(duthost, cp=DEFAULT_CHECKPOINT_NAME): + """Run rollback on target duthost Args: duthost: Device Under Test (DUT) - cp: checkpoint filename + cp: rollback filename """ - cmds = 'config checkpoint {}'.format(cp) + cmds = 'config rollback {}'.format(cp) logger.info("Commands: {}".format(cmds)) output = duthost.shell(cmds, module_ignore_errors=True) - pytest_assert( - not output['rc'] - and "Checkpoint created successfully" in output['stdout'] - and verify_checkpoints_exist(duthost, cp), - "Failed to config a checkpoint file: {}".format(cp) - ) + return output + + +def rollback_or_reload(duthost, cp=DEFAULT_CHECKPOINT_NAME): + """Run rollback on target duthost. config_reload if rollback failed. + + Args: + duthost: Device Under Test (DUT) + """ + output = rollback(duthost, cp) + + if output['rc'] or "Config rolled back successfully" not in output['stdout']: + config_reload(duthost) + pytest.fail("config rollback failed. Restored by config_reload") def delete_checkpoint(duthost, cp=DEFAULT_CHECKPOINT_NAME): @@ -233,35 +132,17 @@ def delete_checkpoint(duthost, cp=DEFAULT_CHECKPOINT_NAME): logger.info("Commands: {}".format(cmds)) output = duthost.shell(cmds, module_ignore_errors=True) - try: - # duthost.template uses single quotes, which breaks apply-patch. this replaces them with double quotes - dut.shell("sed -i \"s/'/\\\"/g\" " + dest_path) - output = dut.shell("config apply-patch {}".format(dest_path)) - outputs.append(output) - finally: - dut.file(path=dest_path, state='absent') - - return outputs - - -def load_and_apply_json_patch(duthost, file_name, setup): - with open(os.path.join(TEMPLATES_DIR, file_name)) as file: - json_patch = json.load(file) - - duts_to_apply = [duthost] - outputs = [] - if setup["is_dualtor"]: - duts_to_apply.append(setup["rand_unselected_dut"]) - - for dut in duts_to_apply: - - tmpfile = generate_tmpfile(dut) - logger.info("tmpfile {}".format(tmpfile)) + pytest_assert( + not output['rc'] and "Checkpoint deleted successfully" in output['stdout'], + "Failed to delete a checkpoint file: {}".format(cp) + ) - try: - output = apply_patch(dut, json_data=json_patch, dest_file=tmpfile) - outputs.append(output) - finally: - delete_tmpfile(dut, tmpfile) - return outputs +def expect_op_success(duthost, output): + """Expected success from apply-patch output + """ + pytest_assert(not output['rc'], "Command is not running successfully") + pytest_assert( + "Patch applied successfully" in output['stdout'], + "Please check if json file is validate" + ) \ No newline at end of file From 0c74b3b1b3799c51de691f10da2beffbd00ff80b Mon Sep 17 00:00:00 2001 From: "Steve Li(Azure)" Date: Mon, 7 Oct 2024 11:40:16 -0700 Subject: [PATCH 10/17] Remove trailing space. --- tests/generic_config_updater/test_multiasic_scenarios.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py index accabdb9282..e8dc514f008 100644 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -194,4 +194,4 @@ def test_check_apply_patch_negative_case(duthost): "Expected failure did not occur as expected. Output: {}".format( output["stderr"] ), - ) + ) \ No newline at end of file From 54171eb5860e825f5045025ce9f5651e0be9e161 Mon Sep 17 00:00:00 2001 From: "Steve Li(Azure)" Date: Tue, 8 Oct 2024 09:34:08 -0700 Subject: [PATCH 11/17] align with master code --- tests/common/gu_utils.py | 5 +- tests/generic_config_updater/gu_utils.py | 170 +++++------------- .../test_multiasic_scenarios.py | 12 +- 3 files changed, 49 insertions(+), 138 deletions(-) diff --git a/tests/common/gu_utils.py b/tests/common/gu_utils.py index 114d2e9a8e9..600010108ea 100644 --- a/tests/common/gu_utils.py +++ b/tests/common/gu_utils.py @@ -31,17 +31,18 @@ def delete_tmpfile(duthost, tmpfile): duthost.file(path=tmpfile, state='absent') -def apply_patch(duthost, json_data, dest_file): +def apply_patch(duthost, json_data, dest_file, ignore_tables=None): """Run apply-patch on target duthost Args: duthost: Device Under Test (DUT) json_data: Source json patch to apply dest_file: Destination file on duthost + ignore_tables: to be ignored tables, "-i table_name" """ duthost.copy(content=json.dumps(json_data, indent=4), dest=dest_file) - cmds = 'config apply-patch {}'.format(dest_file) + cmds = 'config apply-patch {} {}'.format(dest_file, ignore_tables if ignore_tables else "") logger.info("Commands: {}".format(cmds)) output = duthost.shell(cmds, module_ignore_errors=True) diff --git a/tests/generic_config_updater/gu_utils.py b/tests/generic_config_updater/gu_utils.py index 377ffff4c2d..fc236f09901 100644 --- a/tests/generic_config_updater/gu_utils.py +++ b/tests/generic_config_updater/gu_utils.py @@ -1,148 +1,58 @@ -import json +import os import logging +import json +from tests.common.gu_utils import apply_patch, generate_tmpfile, delete_tmpfile -import pytest -from tests.common import config_reload -from tests.common.helpers.assertions import pytest_assert +BASE_DIR = os.path.dirname(os.path.realpath(__file__)) +TEMPLATES_DIR = os.path.join(BASE_DIR, "../generic_config_updater/templates") +TMP_DIR = '/tmp' logger = logging.getLogger(__name__) -DEFAULT_CHECKPOINT_NAME = "test" - - -def generate_tmpfile(duthost): - """Generate temp file - """ - return duthost.shell('mktemp')['stdout'] - - -def apply_patch(duthost, json_data, dest_file, ignore_tables=None): - """Run apply-patch on target duthost - - Args: - duthost: Device Under Test (DUT) - json_data: Source json patch to apply - dest_file: Destination file on duthost - ignore_tables: to be ignored tables, "-i table_name" - """ - duthost.copy(content=json.dumps(json_data, indent=4), dest=dest_file) - - cmds = 'config apply-patch {} {}'.format(dest_file, ignore_tables if ignore_tables else "") - - logger.info("Commands: {}".format(cmds)) - output = duthost.shell(cmds, module_ignore_errors=True) - - return output - - -def delete_tmpfile(duthost, tmpfile): - """Delete temp file - """ - duthost.file(path=tmpfile, state='absent') - - -def create_checkpoint(duthost, cp=DEFAULT_CHECKPOINT_NAME): - """Run checkpoint on target duthost - - Args: - duthost: Device Under Test (DUT) - cp: checkpoint filename - """ - cmds = 'config checkpoint {}'.format(cp) - - logger.info("Commands: {}".format(cmds)) - output = duthost.shell(cmds, module_ignore_errors=True) - - pytest_assert( - not output['rc'] - and "Checkpoint created successfully" in output['stdout'] - and verify_checkpoints_exist(duthost, cp), - "Failed to config a checkpoint file: {}".format(cp) - ) - - -def list_checkpoints(duthost): - """List checkpoint on target duthost - - Args: - duthost: Device Under Test (DUT) - """ - cmds = 'config list-checkpoints' - - logger.info("Commands: {}".format(cmds)) - output = duthost.shell(cmds, module_ignore_errors=True) - - pytest_assert( - not output['rc'], - "Failed to list all checkpoint file" - ) - - return output - - -def verify_checkpoints_exist(duthost, cp): - """Check if checkpoint file exist in duthost - """ - output = list_checkpoints(duthost) - return '"{}"'.format(cp) in output['stdout'] - - -def rollback(duthost, cp=DEFAULT_CHECKPOINT_NAME): - """Run rollback on target duthost - - Args: - duthost: Device Under Test (DUT) - cp: rollback filename - """ - cmds = 'config rollback {}'.format(cp) - - logger.info("Commands: {}".format(cmds)) - output = duthost.shell(cmds, module_ignore_errors=True) - return output +def format_and_apply_template(duthost, template_name, extra_vars, setup): + dest_path = os.path.join(TMP_DIR, template_name) -def rollback_or_reload(duthost, cp=DEFAULT_CHECKPOINT_NAME): - """Run rollback on target duthost. config_reload if rollback failed. + duts_to_apply = [duthost] + outputs = [] + if setup["is_dualtor"]: + duts_to_apply.append(setup["rand_unselected_dut"]) - Args: - duthost: Device Under Test (DUT) - """ - output = rollback(duthost, cp) + for dut in duts_to_apply: + dut.host.options['variable_manager'].extra_vars.update(extra_vars) + dut.file(path=dest_path, state='absent') + dut.template(src=os.path.join(TEMPLATES_DIR, template_name), dest=dest_path) - if output['rc'] or "Config rolled back successfully" not in output['stdout']: - config_reload(duthost) - pytest.fail("config rollback failed. Restored by config_reload") + try: + # duthost.template uses single quotes, which breaks apply-patch. this replaces them with double quotes + dut.shell("sed -i \"s/'/\\\"/g\" " + dest_path) + output = dut.shell("config apply-patch {}".format(dest_path)) + outputs.append(output) + finally: + dut.file(path=dest_path, state='absent') + return outputs -def delete_checkpoint(duthost, cp=DEFAULT_CHECKPOINT_NAME): - """Run checkpoint on target duthost - Args: - duthost: Device Under Test (DUT) - cp: checkpoint filename - """ - pytest_assert( - verify_checkpoints_exist(duthost, cp), - "Failed to find the checkpoint file: {}".format(cp) - ) +def load_and_apply_json_patch(duthost, file_name, setup): + with open(os.path.join(TEMPLATES_DIR, file_name)) as file: + json_patch = json.load(file) - cmds = 'config delete-checkpoint {}'.format(cp) + duts_to_apply = [duthost] + outputs = [] + if setup["is_dualtor"]: + duts_to_apply.append(setup["rand_unselected_dut"]) - logger.info("Commands: {}".format(cmds)) - output = duthost.shell(cmds, module_ignore_errors=True) + for dut in duts_to_apply: - pytest_assert( - not output['rc'] and "Checkpoint deleted successfully" in output['stdout'], - "Failed to delete a checkpoint file: {}".format(cp) - ) + tmpfile = generate_tmpfile(dut) + logger.info("tmpfile {}".format(tmpfile)) + try: + output = apply_patch(dut, json_data=json_patch, dest_file=tmpfile) + outputs.append(output) + finally: + delete_tmpfile(dut, tmpfile) -def expect_op_success(duthost, output): - """Expected success from apply-patch output - """ - pytest_assert(not output['rc'], "Command is not running successfully") - pytest_assert( - "Patch applied successfully" in output['stdout'], - "Please check if json file is validate" - ) \ No newline at end of file + return outputs \ No newline at end of file diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py index e8dc514f008..3b8b6a337f3 100644 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -4,9 +4,9 @@ import re from tests.common.helpers.assertions import pytest_assert -from tests.generic_config_updater.gu_utils import apply_patch -from tests.generic_config_updater.gu_utils import generate_tmpfile, delete_tmpfile -from tests.generic_config_updater.gu_utils import create_checkpoint, delete_checkpoint, rollback_or_reload +from tests.common.gu_utils import apply_patch +from tests.common.gu_utils import generate_tmpfile, delete_tmpfile +from tests.common.gu_utils import create_checkpoint, delete_checkpoint, rollback_or_reload pytestmark = [ pytest.mark.topology('any'), @@ -95,7 +95,7 @@ def test_check_idf_isolation_apply_patch(duthost): tmpfile = generate_tmpfile(duthost) try: - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile, ignore_tables="-i PORT") + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile, ignore_tables="-i /PORT") if output['rc'] or "Patch applied successfully" not in output['stdout']: logger.info("Patching process broken, the error output is {}".format(output['stdout'])) @@ -119,7 +119,7 @@ def test_check_idf_unisolation_apply_patch(duthost): tmpfile = generate_tmpfile(duthost) try: - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile, ignore_tables="-i PORT") + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile, ignore_tables="-i /PORT") if output['rc'] or "Patch applied successfully" not in output['stdout']: logger.info("Patching process broken, the error output is {}".format(output['stdout'])) @@ -152,7 +152,7 @@ def test_check_link_crc_mitigation_remove_and_add_apply_patch(duthost): pytest_assert(redis_value == expected_value, "Config Link CRC Mitigation add action failed.") json_patch = LINK_CRC_MITIGATION_REMOVE_TEMPLATE.format(portchannel, port) - output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile, ignore_tables="-i PORT") + output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile, ignore_tables="-i /PORT") if output['rc'] or "Patch applied successfully" not in output['stdout']: logger.info("Patching process broken, the error output is {}".format(output['stdout'])) From f814e4cffe709eff2e20dd7f1a2feff271659e46 Mon Sep 17 00:00:00 2001 From: "Steve Li(Azure)" Date: Tue, 8 Oct 2024 09:37:49 -0700 Subject: [PATCH 12/17] Fix format --- tests/generic_config_updater/gu_utils.py | 2 +- tests/generic_config_updater/test_multiasic_scenarios.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/generic_config_updater/gu_utils.py b/tests/generic_config_updater/gu_utils.py index fc236f09901..6032b26145f 100644 --- a/tests/generic_config_updater/gu_utils.py +++ b/tests/generic_config_updater/gu_utils.py @@ -55,4 +55,4 @@ def load_and_apply_json_patch(duthost, file_name, setup): finally: delete_tmpfile(dut, tmpfile) - return outputs \ No newline at end of file + return outputs diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py index 3b8b6a337f3..c7f7af3a00f 100644 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -194,4 +194,4 @@ def test_check_apply_patch_negative_case(duthost): "Expected failure did not occur as expected. Output: {}".format( output["stderr"] ), - ) \ No newline at end of file + ) From 4877064254e2dcd5d5a5d998eda1a9591d609058 Mon Sep 17 00:00:00 2001 From: "Steve Li(Azure)" Date: Tue, 8 Oct 2024 09:46:18 -0700 Subject: [PATCH 13/17] Fix format. --- tests/generic_config_updater/test_multiasic_scenarios.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py index c7f7af3a00f..65219d6b129 100644 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -124,7 +124,7 @@ def test_check_idf_unisolation_apply_patch(duthost): if output['rc'] or "Patch applied successfully" not in output['stdout']: logger.info("Patching process broken, the error output is {}".format(output['stdout'])) pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) - + cmds = 'sonic-db-cli -n asic0 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" idf_isolation_state' expected_value = "unisolated" redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] From d24ffa738616aaeb9a4f5fbd2b2ff468b3380be2 Mon Sep 17 00:00:00 2001 From: xincunli-sonic Date: Fri, 22 Nov 2024 11:27:15 -0800 Subject: [PATCH 14/17] Add Debug log --- .../test_multiasic_scenarios.py | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py index 65219d6b129..bfa5d582a75 100644 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ b/tests/generic_config_updater/test_multiasic_scenarios.py @@ -95,7 +95,12 @@ def test_check_idf_isolation_apply_patch(duthost): tmpfile = generate_tmpfile(duthost) try: - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile, ignore_tables="-i /PORT") + print("The current running config is:") + print(duthost.shell("show run all", module_ignore_errors=False)['stdout']) + logger.info("The current running config is:") + logger.info(duthost.shell("show run all", module_ignore_errors=False)['stdout']) + + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) if output['rc'] or "Patch applied successfully" not in output['stdout']: logger.info("Patching process broken, the error output is {}".format(output['stdout'])) @@ -119,7 +124,12 @@ def test_check_idf_unisolation_apply_patch(duthost): tmpfile = generate_tmpfile(duthost) try: - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile, ignore_tables="-i /PORT") + print("The current running config is:") + print(duthost.shell("show run all", module_ignore_errors=False)['stdout']) + logger.info("The current running config is:") + logger.info(duthost.shell("show run all", module_ignore_errors=False)['stdout']) + + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) if output['rc'] or "Patch applied successfully" not in output['stdout']: logger.info("Patching process broken, the error output is {}".format(output['stdout'])) @@ -142,6 +152,11 @@ def test_check_link_crc_mitigation_remove_and_add_apply_patch(duthost): tmpfile = generate_tmpfile(duthost) try: + print("The current running config is:") + print(duthost.shell("show run all", module_ignore_errors=False)['stdout']) + logger.info("The current running config is:") + logger.info(duthost.shell("show run all", module_ignore_errors=False)['stdout']) + result = duthost.shell("show interfaces portchannel -n asic0", module_ignore_errors=False)['stdout'] portchannel, port = extract_up_interface(result) @@ -152,7 +167,7 @@ def test_check_link_crc_mitigation_remove_and_add_apply_patch(duthost): pytest_assert(redis_value == expected_value, "Config Link CRC Mitigation add action failed.") json_patch = LINK_CRC_MITIGATION_REMOVE_TEMPLATE.format(portchannel, port) - output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile, ignore_tables="-i /PORT") + output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile) if output['rc'] or "Patch applied successfully" not in output['stdout']: logger.info("Patching process broken, the error output is {}".format(output['stdout'])) @@ -183,6 +198,11 @@ def test_check_apply_patch_negative_case(duthost): tmpfile = generate_tmpfile(duthost) try: + print("The current running config is:") + print(duthost.shell("show run all", module_ignore_errors=False)['stdout']) + logger.info("The current running config is:") + logger.info(duthost.shell("show run all", module_ignore_errors=False)['stdout']) + output = apply_patch( duthost, json_data=json.loads(json_patch), dest_file=tmpfile ) From 4819365551469d4d2ac2762dd1764524b5ef1552 Mon Sep 17 00:00:00 2001 From: Xincun Li Date: Thu, 12 Dec 2024 14:41:42 -0800 Subject: [PATCH 15/17] Address comments. --- .../test_multiasic_idf.py | 210 +++++++++++++++++ .../test_multiasic_linkcrc.py | 122 ++++++++++ .../test_multiasic_scenarios.py | 217 ------------------ 3 files changed, 332 insertions(+), 217 deletions(-) create mode 100644 tests/generic_config_updater/test_multiasic_idf.py create mode 100644 tests/generic_config_updater/test_multiasic_linkcrc.py delete mode 100644 tests/generic_config_updater/test_multiasic_scenarios.py diff --git a/tests/generic_config_updater/test_multiasic_idf.py b/tests/generic_config_updater/test_multiasic_idf.py new file mode 100644 index 00000000000..b3691b6ea1d --- /dev/null +++ b/tests/generic_config_updater/test_multiasic_idf.py @@ -0,0 +1,210 @@ +import logging +import pytest +from tests.common.helpers.assertions import pytest_assert +from tests.common.gu_utils import apply_patch +from tests.common.gu_utils import generate_tmpfile, delete_tmpfile +from tests.common.gu_utils import (create_checkpoint, delete_checkpoint, rollback_or_reload) + +pytestmark = [ + pytest.mark.topology('any'), +] + +logger = logging.getLogger(__name__) + + +def apply_patch_and_verify(duthost, json_patch, tmpfile): + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + if output['rc'] or "Patch applied successfully" not in output['stdout']: + err_msg = f"Patching failed: {output['stdout']}" + logger.info(err_msg) + pytest_assert(False, err_msg) + return output + + +def verify_asic_state(duthost, asic_id, expected_state): + cmds = (f'sonic-db-cli -n asic{asic_id} CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" idf_isolation_state') + redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] + pytest_assert(redis_value == expected_state, f"Config IDF ISOLATION failed for asic{asic_id}") + + +def verify_idf_status(duthost, expected_states): + if duthost.facts['router_type'] != 'spinerouter': + return + status_output = duthost.shell("sudo idf_isolation status", module_ignore_errors=False)['stdout'] + + if isinstance(expected_states, dict): + expected_lines = [ + f"BGP{asic_id}: IDF isolation state: {state}" + for asic_id, state in expected_states.items() + ] + else: + expected_lines = expected_states + + for line in expected_lines: + pytest_assert( + line in status_output, + f"IDF isolation status check failed: {line} not found" + ) + + +@pytest.fixture(autouse=True) +def setup_env(duthosts, rand_one_dut_hostname): + """Setup/teardown fixture for each test""" + duthost = duthosts[rand_one_dut_hostname] + create_checkpoint(duthost) + yield + try: + logger.info("Rolled back to original checkpoint") + rollback_or_reload(duthost) + finally: + delete_checkpoint(duthost) + + +@pytest.fixture +def setup_tmpfile(duthost): + """Fixture to handle tmpfile creation/cleanup""" + tmpfile = generate_tmpfile(duthost) + yield tmpfile + delete_tmpfile(duthost, tmpfile) + + +test_params = [ + pytest.param( + [], + None, + id="empty_patch" + ), + pytest.param( + [ + { + "op": "add", + "path": "/asic0/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", + "value": "isolated_no_export" + }, + { + "op": "add", + "path": "/asic1/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", + "value": "isolated_withdraw_all" + } + ], + { + 0: "isolated_no_export", + 1: "isolated_withdraw_all" + }, + id="basic_isolation" + ), + pytest.param( + [ + { + "op": "add", + "path": f"/asic{i}/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", + "value": "unisolated" + } + for i in [0, 1] + ], + { + 0: "unisolated", + 1: "unisolated" + }, + id="unisolation" + ), + pytest.param( + [ + { + "op": "add", + "path": f"/asic{i}/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", + "value": "isolated_no_export" + } + for i in [0, 1] + ], + { + 0: "isolated_no_export", + 1: "isolated_no_export" + }, + id="no_export_all" + ) +] + + +@pytest.mark.parametrize("json_patch,expected_states", test_params) +def test_idf_isolation_states(duthost, setup_tmpfile, json_patch, expected_states): + """Parameterized test for various IDF isolation states""" + tmpfile = setup_tmpfile + + # For basic isolation test, show current config + if expected_states and 0 in expected_states: + if (expected_states[0] == "isolated_no_export" and + expected_states[1] == "isolated_withdraw_all"): + logger.info("The current running config is:") + logger.info( + duthost.shell("show run all", module_ignore_errors=False)['stdout'] + ) + + apply_patch_and_verify(duthost, json_patch, tmpfile) + + if expected_states: + # Verify states for each ASIC + for asic_id, expected_state in expected_states.items(): + verify_asic_state(duthost, asic_id, expected_state) + # Verify type of expected_states + pytest_assert(isinstance(expected_states, dict), "expected_states must be a dictionary") + # Verify status output + verify_idf_status(duthost, expected_states) + + +# Mixed states test cases +mixed_states_params = [ + # asic0: no_export, asic1: withdraw_all + { + "patch": [ + { + "op": "add", + "path": "/asic0/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", + "value": "isolated_no_export" + }, + { + "op": "add", + "path": "/asic1/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", + "value": "isolated_withdraw_all" + } + ], + "expected_states": { + 0: "isolated_no_export", + 1: "isolated_withdraw_all" + } + }, + # asic0: withdraw_all, asic1: no_export + { + "patch": [ + { + "op": "add", + "path": "/asic0/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", + "value": "isolated_withdraw_all" + }, + { + "op": "add", + "path": "/asic1/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", + "value": "isolated_no_export" + } + ], + "expected_states": { + 0: "isolated_withdraw_all", + 1: "isolated_no_export" + } + } +] + + +@pytest.mark.parametrize("test_case", mixed_states_params) +def test_idf_isolation_mixed_states(duthost, setup_tmpfile, test_case): + """Test different isolation states on different ASICs""" + tmpfile = setup_tmpfile + + apply_patch_and_verify(duthost, test_case["patch"], tmpfile) + + # Verify states + for asic_id, expected_state in test_case["expected_states"].items(): + verify_asic_state(duthost, asic_id, expected_state) + + # Verify status output + verify_idf_status(duthost, test_case["expected_states"]) diff --git a/tests/generic_config_updater/test_multiasic_linkcrc.py b/tests/generic_config_updater/test_multiasic_linkcrc.py new file mode 100644 index 00000000000..6aaee88877e --- /dev/null +++ b/tests/generic_config_updater/test_multiasic_linkcrc.py @@ -0,0 +1,122 @@ +import json +import logging +import pytest +import re + +from tests.common.helpers.assertions import pytest_assert +from tests.common.gu_utils import apply_patch +from tests.common.gu_utils import generate_tmpfile, delete_tmpfile +from tests.common.gu_utils import create_checkpoint, delete_checkpoint, rollback_or_reload + +pytestmark = [ + pytest.mark.topology('any'), +] + +logger = logging.getLogger(__name__) + +LINK_CRC_MITIGATION_ADD_TEMPLATE = '[{{"op": "add", "path": "/asic0/PORTCHANNEL_MEMBER/{}|{}", "value": {}}}]' +LINK_CRC_MITIGATION_REMOVE_TEMPLATE = '[{{"op": "remove", "path": "/asic0/PORTCHANNEL_MEMBER/{}|{}"}}]' + + +def extract_up_interface(output): + """Extract portchannel and port from interface output.""" + pattern = re.compile( + r"^\s*(\d+)\s+(PortChannel\d+)\s+LACP\(\w+\)\(Up\)\s+(Ethernet\d+)\([US]\)", + re.MULTILINE + ) + match = pattern.search(output) + if match: + return match.group(2), match.group(3) + return None, None + + +def apply_patch_and_verify(duthost, json_patch, tmpfile): + """Apply patch and verify success.""" + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + if output['rc'] or "Patch applied successfully" not in output['stdout']: + err_msg = f"Patching failed: {output['stdout']}" + logger.info(err_msg) + pytest_assert(False, err_msg) + return output + + +def verify_portchannel_member(duthost, portchannel, port, expected_value): + """Verify portchannel member state in CONFIG_DB.""" + cmds = f'sonic-db-cli -n asic0 CONFIG_DB keys "PORTCHANNEL_MEMBER|{portchannel}|{port}"' + redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'].strip() + if expected_value: + expected_value = f"PORTCHANNEL_MEMBER|{portchannel}|{port}" + pytest_assert(redis_value == expected_value, "Config Link CRC Mitigation action failed") + + +def show_current_config(duthost): + """Show current running config.""" + logger.info("The current running config is:") + logger.info(duthost.shell("show run all", module_ignore_errors=False)['stdout']) + + +@pytest.fixture(autouse=True) +def setup_env(duthosts, rand_one_dut_hostname): + """Setup/teardown fixture for each multi asic test.""" + duthost = duthosts[rand_one_dut_hostname] + create_checkpoint(duthost) + yield + try: + logger.info("Rolled back to original checkpoint") + rollback_or_reload(duthost) + finally: + delete_checkpoint(duthost) + + +def test_check_empty_apply_patch(duthost): + """Test applying empty patch.""" + json_patch = [] + tmpfile = generate_tmpfile(duthost) + + try: + apply_patch_and_verify(duthost, json_patch, tmpfile) + finally: + delete_tmpfile(duthost, tmpfile) + + +def test_check_link_crc_mitigation_remove_and_add_apply_patch(duthost): + """Test removing and adding link CRC mitigation.""" + tmpfile = generate_tmpfile(duthost) + try: + show_current_config(duthost) + + result = duthost.shell("show interfaces portchannel -n asic0", module_ignore_errors=False)['stdout'] + portchannel, port = extract_up_interface(result) + + # Verify initial state + verify_portchannel_member(duthost, portchannel, port, True) + + # Remove member + json_patch = LINK_CRC_MITIGATION_REMOVE_TEMPLATE.format(portchannel, port) + apply_patch_and_verify(duthost, json.loads(json_patch), tmpfile) + verify_portchannel_member(duthost, portchannel, port, False) + + # Add member back + json_patch = LINK_CRC_MITIGATION_ADD_TEMPLATE.format(portchannel, port, "{}") + apply_patch_and_verify(duthost, json.loads(json_patch), tmpfile) + verify_portchannel_member(duthost, portchannel, port, True) + + finally: + delete_tmpfile(duthost, tmpfile) + + +def test_check_apply_patch_negative_case(duthost): + """Test patch failure case.""" + json_patch = '[{"op": "replace", "path": "/x"}]' + tmpfile = generate_tmpfile(duthost) + + try: + show_current_config(duthost) + output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile) + finally: + delete_tmpfile(duthost, tmpfile) + + pytest_assert( + output["rc"] != 0 and "Failed to apply patch" in output["stderr"], + f"Expected failure did not occur as expected. Output: {output['stderr']}" + ) diff --git a/tests/generic_config_updater/test_multiasic_scenarios.py b/tests/generic_config_updater/test_multiasic_scenarios.py deleted file mode 100644 index bfa5d582a75..00000000000 --- a/tests/generic_config_updater/test_multiasic_scenarios.py +++ /dev/null @@ -1,217 +0,0 @@ -import json -import logging -import pytest -import re - -from tests.common.helpers.assertions import pytest_assert -from tests.common.gu_utils import apply_patch -from tests.common.gu_utils import generate_tmpfile, delete_tmpfile -from tests.common.gu_utils import create_checkpoint, delete_checkpoint, rollback_or_reload - -pytestmark = [ - pytest.mark.topology('any'), -] - -logger = logging.getLogger(__name__) - -IDF_ISOLATION = [ - { - "op": "add", - "path": "/asic0/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", - "value": "isolated_no_export" - }, - { - "op": "add", - "path": "/asic1/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", - "value": "isolated_withdraw_all" - }, -] - -IDF_UNISOLATION = [ - { - "op": "add", - "path": "/asic0/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", - "value": "unisolated" - }, - { - "op": "add", - "path": "/asic1/BGP_DEVICE_GLOBAL/STATE/idf_isolation_state", - "value": "unisolated" - }, -] - -LINK_CRC_MITIGATION_REMOVE_TEMPLATE = '[{{"op": "remove", "path": "/asic0/PORTCHANNEL_MEMBER/{}|{}"}}]' -LINK_CRC_MITIGATION_ADD_TEMPLATE = '[{{"op": "add", "path": "/asic0/PORTCHANNEL_MEMBER/{}|{}", "value": {}}}]' - - -def extract_up_interface(output): - # Updated regex pattern to match both (U) and (S) status - pattern = re.compile(r"^\s*(\d+)\s+(PortChannel\d+)\s+LACP\(\w+\)\(Up\)\s+(Ethernet\d+)\([US]\)", re.MULTILINE) - match = pattern.search(output) - if match: - return match.group(2), match.group(3) - else: - return None, None - - -@pytest.fixture(autouse=True) -def setup_env(duthosts, rand_one_dut_hostname): - """ - Setup/teardown fixture for each multi asic test. - rollback to check if it goes back to starting config - Args: - duthosts: list of DUTs. - rand_selected_dut: The fixture returns a randomly selected DuT. - """ - duthost = duthosts[rand_one_dut_hostname] - - create_checkpoint(duthost) - - yield - - try: - logger.info("Rolled back to original checkpoint") - rollback_or_reload(duthost) - finally: - delete_checkpoint(duthost) - - -def test_check_empty_apply_patch(duthost): - json_patch = [] - tmpfile = generate_tmpfile(duthost) - - try: - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) - finally: - delete_tmpfile(duthost, tmpfile) - - if output['rc'] or "Patch applied successfully" not in output['stdout']: - logger.info("Patching process broken, the error output is {}").format(output['stdout']) - pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) - - -def test_check_idf_isolation_apply_patch(duthost): - json_patch = IDF_ISOLATION - tmpfile = generate_tmpfile(duthost) - - try: - print("The current running config is:") - print(duthost.shell("show run all", module_ignore_errors=False)['stdout']) - logger.info("The current running config is:") - logger.info(duthost.shell("show run all", module_ignore_errors=False)['stdout']) - - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) - - if output['rc'] or "Patch applied successfully" not in output['stdout']: - logger.info("Patching process broken, the error output is {}".format(output['stdout'])) - pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) - - cmds = 'sonic-db-cli -n asic0 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" idf_isolation_state' - expected_value = "isolated_no_export" - redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] - pytest_assert(redis_value == expected_value, "Config IDF ISOLATION failed") - - cmds = 'sonic-db-cli -n asic1 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" "idf_isolation_state"' - expected_value = "isolated_withdraw_all" - redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] - pytest_assert(redis_value == expected_value, "Config IDF ISOLATION failed") - finally: - delete_tmpfile(duthost, tmpfile) - - -def test_check_idf_unisolation_apply_patch(duthost): - json_patch = IDF_UNISOLATION - tmpfile = generate_tmpfile(duthost) - - try: - print("The current running config is:") - print(duthost.shell("show run all", module_ignore_errors=False)['stdout']) - logger.info("The current running config is:") - logger.info(duthost.shell("show run all", module_ignore_errors=False)['stdout']) - - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) - - if output['rc'] or "Patch applied successfully" not in output['stdout']: - logger.info("Patching process broken, the error output is {}".format(output['stdout'])) - pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) - - cmds = 'sonic-db-cli -n asic0 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" idf_isolation_state' - expected_value = "unisolated" - redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] - pytest_assert(redis_value == expected_value, "Config IDF ISOLATION failed") - - cmds = 'sonic-db-cli -n asic1 CONFIG_DB hget "BGP_DEVICE_GLOBAL|STATE" idf_isolation_state' - expected_value = "unisolated" - redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] - pytest_assert(redis_value == expected_value, "Config IDF ISOLATION failed") - finally: - delete_tmpfile(duthost, tmpfile) - - -def test_check_link_crc_mitigation_remove_and_add_apply_patch(duthost): - tmpfile = generate_tmpfile(duthost) - - try: - print("The current running config is:") - print(duthost.shell("show run all", module_ignore_errors=False)['stdout']) - logger.info("The current running config is:") - logger.info(duthost.shell("show run all", module_ignore_errors=False)['stdout']) - - result = duthost.shell("show interfaces portchannel -n asic0", module_ignore_errors=False)['stdout'] - portchannel, port = extract_up_interface(result) - - # Precheck keys existing - cmds = 'sonic-db-cli -n asic0 CONFIG_DB keys "PORTCHANNEL_MEMBER|{}|{}"'.format(portchannel, port) - expected_value = "PORTCHANNEL_MEMBER|{}|{}".format(portchannel, port) - redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] - pytest_assert(redis_value == expected_value, "Config Link CRC Mitigation add action failed.") - - json_patch = LINK_CRC_MITIGATION_REMOVE_TEMPLATE.format(portchannel, port) - output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile) - - if output['rc'] or "Patch applied successfully" not in output['stdout']: - logger.info("Patching process broken, the error output is {}".format(output['stdout'])) - pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) - - cmds = 'sonic-db-cli -n asic0 CONFIG_DB keys "PORTCHANNEL_MEMBER|{}|{}"'.format(portchannel, port) - expected_value = "" - redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] - pytest_assert(redis_value.strip() == expected_value, "Config Link CRC Mitigation remove action failed.") - - json_patch = LINK_CRC_MITIGATION_ADD_TEMPLATE.format(portchannel, port, "{}") - output = apply_patch(duthost, json_data=json.loads(json_patch), dest_file=tmpfile) - - if output['rc'] or "Patch applied successfully" not in output['stdout']: - logger.info("Patching process broken, the error output is {}".format(output['stdout'])) - pytest_assert(False, "Patching process broken, the error output is {}").format(output['stdout']) - - cmds = 'sonic-db-cli -n asic0 CONFIG_DB keys "PORTCHANNEL_MEMBER|{}|{}"'.format(portchannel, port) - expected_value = "PORTCHANNEL_MEMBER|{}|{}".format(portchannel, port) - redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'] - pytest_assert(redis_value == expected_value, "Config Link CRC Mitigation add action failed.") - finally: - delete_tmpfile(duthost, tmpfile) - - -def test_check_apply_patch_negative_case(duthost): - json_patch = '[{"op": "replace", "path": "/x"}]' - tmpfile = generate_tmpfile(duthost) - - try: - print("The current running config is:") - print(duthost.shell("show run all", module_ignore_errors=False)['stdout']) - logger.info("The current running config is:") - logger.info(duthost.shell("show run all", module_ignore_errors=False)['stdout']) - - output = apply_patch( - duthost, json_data=json.loads(json_patch), dest_file=tmpfile - ) - finally: - delete_tmpfile(duthost, tmpfile) - - pytest_assert( - output["rc"] != 0 and "Failed to apply patch" in output["stderr"], - "Expected failure did not occur as expected. Output: {}".format( - output["stderr"] - ), - ) From a16e39df0ca5fe189af1884eb7c5231713dd07ff Mon Sep 17 00:00:00 2001 From: Xincun Li Date: Fri, 13 Dec 2024 09:47:02 -0800 Subject: [PATCH 16/17] Add test to multi-asic-t1-lag --- .azure-pipelines/pr_test_scripts.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.azure-pipelines/pr_test_scripts.yaml b/.azure-pipelines/pr_test_scripts.yaml index 4726e674d12..7ea4d3e6178 100644 --- a/.azure-pipelines/pr_test_scripts.yaml +++ b/.azure-pipelines/pr_test_scripts.yaml @@ -458,7 +458,8 @@ multi-asic-t1-lag: - http/test_http_copy.py - telemetry/test_telemetry_cert_rotation.py - telemetry/test_telemetry.py - - generic_config_updater/test_multiasic_scenarios.py + - generic_config_updater/test_multiasic_idf.py + - generic_config_updater/test_multiasic_linkcrc.py dpu: From deb598160b3c85c1190fccb170a3d63795ebf707 Mon Sep 17 00:00:00 2001 From: Xincun Li Date: Fri, 13 Dec 2024 11:49:52 -0800 Subject: [PATCH 17/17] fix verify linkcrc member --- .../test_multiasic_linkcrc.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/generic_config_updater/test_multiasic_linkcrc.py b/tests/generic_config_updater/test_multiasic_linkcrc.py index 6aaee88877e..a3231a9f215 100644 --- a/tests/generic_config_updater/test_multiasic_linkcrc.py +++ b/tests/generic_config_updater/test_multiasic_linkcrc.py @@ -40,13 +40,20 @@ def apply_patch_and_verify(duthost, json_patch, tmpfile): return output -def verify_portchannel_member(duthost, portchannel, port, expected_value): - """Verify portchannel member state in CONFIG_DB.""" +def verify_portchannel_member(duthost, portchannel, port, member_exists): + """Verify portchannel member state in CONFIG_DB. + + Args: + duthost: DUT host object + portchannel: Name of the portchannel + port: Name of the member port + member_exists: Boolean indicating if member should exist + """ cmds = f'sonic-db-cli -n asic0 CONFIG_DB keys "PORTCHANNEL_MEMBER|{portchannel}|{port}"' redis_value = duthost.shell(cmds, module_ignore_errors=False)['stdout'].strip() - if expected_value: - expected_value = f"PORTCHANNEL_MEMBER|{portchannel}|{port}" - pytest_assert(redis_value == expected_value, "Config Link CRC Mitigation action failed") + expected_value = f"PORTCHANNEL_MEMBER|{portchannel}|{port}" if member_exists else "" + pytest_assert(redis_value == expected_value, + f"Config Link CRC Mitigation action failed. Expected: {expected_value}, Got: {redis_value}") def show_current_config(duthost):