From 10f5cbbfd26da215f8bf1ce3e014f3bfff96c9aa Mon Sep 17 00:00:00 2001 From: Jingwen Xie Date: Mon, 6 Dec 2021 08:40:38 +0000 Subject: [PATCH 1/7] GCU loopback interface test --- .../test_lo_interface.py | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 tests/generic_config_updater/test_lo_interface.py diff --git a/tests/generic_config_updater/test_lo_interface.py b/tests/generic_config_updater/test_lo_interface.py new file mode 100644 index 00000000000..caaf9d00685 --- /dev/null +++ b/tests/generic_config_updater/test_lo_interface.py @@ -0,0 +1,174 @@ +import logging +import pytest + +from tests.common.helpers.assertions import pytest_assert +from tests.common.config_reload import config_reload +from tests.generic_config_updater.gu_utils import apply_patch, expect_op_success, expect_op_failure +from tests.generic_config_updater.gu_utils import generate_tmpfile, delete_tmpfile + +pytestmark = [ + pytest.mark.topology('any'), +] + +logger = logging.getLogger(__name__) + +@pytest.fixture(scope="module", autouse=True) +def setup_env(duthosts, rand_one_dut_hostname, cfg_facts): + """ + Setup/teardown fixture for loopback interface config + Args: + duthosts: list of DUTs. + rand_selected_dut: The fixture returns a randomly selected DuT. + cfg_facts: config facts for selected DUT + """ + duthost = duthosts[rand_one_dut_hostname] + + config_tmpfile = generate_tmpfile(duthost) + logger.info("config_tmpfile {} Backing up config_db.json".format(config_tmpfile)) + duthost.shell("sudo cp /etc/sonic/config_db.json {}".format(config_tmpfile)) + + # Cleanup LOOPBACK_INTERFACE config + lo_interfaces = cfg_facts.get('LOOPBACK_INTERFACE', {}) + for lo_interface in lo_interfaces: + del_loopback_interface = duthost.shell("sudo config loopback del {}".format(lo_interface), + module_ignore_errors=True) + pytest_assert(not del_loopback_interface['rc'], + "Loopback interface '{}' is not deleted successfully".format(lo_interface)) + + yield + + logger.info("Restoring config_db.json") + duthost.shell("sudo cp {} /etc/sonic/config_db.json".format(config_tmpfile)) + delete_tmpfile(duthost, config_tmpfile) + config_reload(duthost) + +@pytest.mark.parametrize("op, name, dummy_lo_interface_v4, dummy_lo_interface_v6", [ + ("add", "Loopback0", "10.1.0.32/32", "FC00:1::32/128") +]) +def test_syslog_server_tc1_add_init(duthost, op, name, + dummy_lo_interface_v4, dummy_lo_interface_v6): + """ Add v4 and v6 lo intf to config + + Sample output + "LOOPBACK_INTERFACE": { + "Loopback0": {}, + "Loopback0|10.1.0.32/32": {}, + "Loopback0|FC00:1::32/128": {} + } + """ + dummy_lo_interface_v4 = name + "|" + dummy_lo_interface_v4 + dummy_lo_interface_v6 = name + "|" + dummy_lo_interface_v6 + + json_patch = [ + { + "op": "{}".format(op), + "path": "/LOOPBACK_INTERFACE", + "value": { + "{}".format(name): {}, + "{}".format(dummy_lo_interface_v4): {}, + "{}".format(dummy_lo_interface_v6): {} + } + } + ] + + logger.info("json patch {}".format(json_patch)) + + tmpfile = generate_tmpfile(duthost) + logger.info("tmpfile {}".format(tmpfile)) + + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + expect_op_success(duthost, output) + + delete_tmpfile(duthost, tmpfile) + +@pytest.mark.parametrize("op, name, dummy_lo_interface_v4, dummy_lo_interface_v6", [ + ("add", "Loopback0", "10.1.0.32~132", "FC00:1::32~1128") +]) +def test_syslog_server_tc2_add_duplicate(duthost, op, name, + dummy_lo_interface_v4, dummy_lo_interface_v6): + """ Add v4 and v6 duplicate lo intf to config + """ + dummy_lo_interface_v4 = name + "|" + dummy_lo_interface_v4 + dummy_lo_interface_v6 = name + "|" + dummy_lo_interface_v6 + + json_patch = [ + { + "op": "{}".format(op), + "path": "/LOOPBACK_INTERFACE/{}".format(dummy_lo_interface_v4), + "value": {} + }, + { + "op": "{}".format(op), + "path": "/LOOPBACK_INTERFACE/{}".format(dummy_lo_interface_v6), + "value": {} + } + ] + + tmpfile = generate_tmpfile(duthost) + logger.info("tmpfile {}".format(tmpfile)) + + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + expect_op_success(duthost, output) + + delete_tmpfile(duthost, tmpfile) + +@pytest.mark.parametrize("op, name, dummy_lo_interface_v4, dummy_lo_interface_v6", [ + ("add", "Loopback0", "587.1.0.32~132", "FC00:1::32~1128"), + ("add", "Loopback0", "10.1.0.32~132", "FC00:1::xyz~1128"), + ("remove", "Loopback0", "10.1.0.33~132", "FC00:1::32~1128"), + ("remove", "Loopback0", "10.1.0.32~132", "FC00:1::33~1128") +]) +def test_syslog_server_tc3_xfail(duthost, op, name, + dummy_lo_interface_v4, dummy_lo_interface_v6): + """ Test expect fail testcase + + ("add", "Loopback0", "587.1.0.32~132", "FC00:1::32~1128"), ADD Invalid IPv4 address + ("add", "Loopback0", "10.1.0.32~132", "FC00:1::xyz~1128"), ADD Invalid IPv6 address + ("remove", "Loopback0", "10.1.0.33~132", "FC00:1::32~1128"), REMOVE Unexist IPv4 address + ("remove", "Loopback0", "10.1.0.32~132", "FC00:1::33~1128") REMOVE Unexist IPv6 address + """ + dummy_lo_interface_v4 = name + "|" + dummy_lo_interface_v4 + dummy_lo_interface_v6 = name + "|" + dummy_lo_interface_v6 + + json_patch = [ + { + "op": "{}".format(op), + "path": "/LOOPBACK_INTERFACE/{}".format(dummy_lo_interface_v4), + "value": {} + }, + { + "op": "{}".format(op), + "path": "/LOOPBACK_INTERFACE/{}".format(dummy_lo_interface_v6), + "value": {} + } + ] + + tmpfile = generate_tmpfile(duthost) + logger.info("tmpfile {}".format(tmpfile)) + + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + expect_op_failure(output) + + delete_tmpfile(duthost, tmpfile) + +@pytest.mark.parametrize("op, name, dummy_lo_interface_v4, dummy_lo_interface_v6", [ + ("remove", "Loopback0", "10.1.0.32~132", "FC00:1::32~1128") +]) +def test_syslog_server_tc4_remove(duthost, op, name, + dummy_lo_interface_v4, dummy_lo_interface_v6): + """ Remove v4 and v6 loopback intf + """ + json_patch = [ + { + "op": "{}".format(op), + "path": "/LOOPBACK_INTERFACE" + } + ] + + tmpfile = generate_tmpfile(duthost) + logger.info("tmpfile {}".format(tmpfile)) + + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + expect_op_success(duthost, output) + + delete_tmpfile(duthost, tmpfile) From d351592ab4ca979563f9c4499fe5f78cb9c806e2 Mon Sep 17 00:00:00 2001 From: Jingwen Xie Date: Fri, 10 Dec 2021 03:32:01 +0000 Subject: [PATCH 2/7] make each test independent and resolve comment --- .../test_lo_interface.py | 128 +++++++++++------- 1 file changed, 79 insertions(+), 49 deletions(-) diff --git a/tests/generic_config_updater/test_lo_interface.py b/tests/generic_config_updater/test_lo_interface.py index caaf9d00685..644e1b27a98 100644 --- a/tests/generic_config_updater/test_lo_interface.py +++ b/tests/generic_config_updater/test_lo_interface.py @@ -6,28 +6,35 @@ from tests.generic_config_updater.gu_utils import apply_patch, expect_op_success, expect_op_failure from tests.generic_config_updater.gu_utils import generate_tmpfile, delete_tmpfile +# Test on t0 topo to verify functionality and to choose predefined variable +# "LOOPBACK_INTERFACE": { + # "Loopback0": {}, + # "Loopback0|10.1.0.32/32": {}, + # "Loopback0|FC00:1::32/128": {} +# } pytestmark = [ - pytest.mark.topology('any'), + pytest.mark.topology('t0'), ] logger = logging.getLogger(__name__) -@pytest.fixture(scope="module", autouse=True) -def setup_env(duthosts, rand_one_dut_hostname, cfg_facts): +@pytest.fixture(autouse=True) +def setup_env(duthosts, rand_one_dut_hostname): """ - Setup/teardown fixture for loopback interface config + Setup/teardown fixture for each loopback interface test Args: duthosts: list of DUTs. rand_selected_dut: The fixture returns a randomly selected DuT. - cfg_facts: config facts for selected DUT """ duthost = duthosts[rand_one_dut_hostname] - config_tmpfile = generate_tmpfile(duthost) - logger.info("config_tmpfile {} Backing up config_db.json".format(config_tmpfile)) - duthost.shell("sudo cp /etc/sonic/config_db.json {}".format(config_tmpfile)) + yield + + logger.info("Restoring config_db.json") + config_reload(duthost) - # Cleanup LOOPBACK_INTERFACE config + # Cleanup LOOPBACK_INTERFACE config +def cleanup_lo_interface_config(duthost, cfg_facts): lo_interfaces = cfg_facts.get('LOOPBACK_INTERFACE', {}) for lo_interface in lo_interfaces: del_loopback_interface = duthost.shell("sudo config loopback del {}".format(lo_interface), @@ -35,38 +42,26 @@ def setup_env(duthosts, rand_one_dut_hostname, cfg_facts): pytest_assert(not del_loopback_interface['rc'], "Loopback interface '{}' is not deleted successfully".format(lo_interface)) - yield - - logger.info("Restoring config_db.json") - duthost.shell("sudo cp {} /etc/sonic/config_db.json".format(config_tmpfile)) - delete_tmpfile(duthost, config_tmpfile) - config_reload(duthost) +def test_lo_interface_tc1_add_init(duthost, cfg_facts): + """ Clean up orig lo interface and test initial addion of v4 and v6 lo intf -@pytest.mark.parametrize("op, name, dummy_lo_interface_v4, dummy_lo_interface_v6", [ - ("add", "Loopback0", "10.1.0.32/32", "FC00:1::32/128") -]) -def test_syslog_server_tc1_add_init(duthost, op, name, - dummy_lo_interface_v4, dummy_lo_interface_v6): - """ Add v4 and v6 lo intf to config - - Sample output + Expected output "LOOPBACK_INTERFACE": { "Loopback0": {}, "Loopback0|10.1.0.32/32": {}, "Loopback0|FC00:1::32/128": {} } """ - dummy_lo_interface_v4 = name + "|" + dummy_lo_interface_v4 - dummy_lo_interface_v6 = name + "|" + dummy_lo_interface_v6 + cleanup_lo_interface_config(duthost, cfg_facts) json_patch = [ { - "op": "{}".format(op), + "op": "add", "path": "/LOOPBACK_INTERFACE", "value": { - "{}".format(name): {}, - "{}".format(dummy_lo_interface_v4): {}, - "{}".format(dummy_lo_interface_v6): {} + "Loopback0": {}, + "Loopback0|10.1.0.32/32": {}, + "Loopback0|FC00:1::32/128": {} } } ] @@ -81,25 +76,26 @@ def test_syslog_server_tc1_add_init(duthost, op, name, delete_tmpfile(duthost, tmpfile) -@pytest.mark.parametrize("op, name, dummy_lo_interface_v4, dummy_lo_interface_v6", [ - ("add", "Loopback0", "10.1.0.32~132", "FC00:1::32~1128") -]) -def test_syslog_server_tc2_add_duplicate(duthost, op, name, - dummy_lo_interface_v4, dummy_lo_interface_v6): +def test_lo_interface_tc2_add_duplicate(duthost): """ Add v4 and v6 duplicate lo intf to config - """ - dummy_lo_interface_v4 = name + "|" + dummy_lo_interface_v4 - dummy_lo_interface_v6 = name + "|" + dummy_lo_interface_v6 + Note: the Identifier '/' as changed to '~1' + Initial Loopback setup in t0 + "LOOPBACK_INTERFACE": { + "Loopback0": {}, + "Loopback0|10.1.0.32/32": {}, + "Loopback0|FC00:1::32/128": {} + } + """ json_patch = [ { - "op": "{}".format(op), - "path": "/LOOPBACK_INTERFACE/{}".format(dummy_lo_interface_v4), + "op": "add", + "path": "/LOOPBACK_INTERFACE/Loopback0|10.1.0.32~132", "value": {} }, { - "op": "{}".format(op), - "path": "/LOOPBACK_INTERFACE/{}".format(dummy_lo_interface_v6), + "op": "add", + "path": "/LOOPBACK_INTERFACE/Loopback0|FC00:1::32~1128", "value": {} } ] @@ -118,7 +114,7 @@ def test_syslog_server_tc2_add_duplicate(duthost, op, name, ("remove", "Loopback0", "10.1.0.33~132", "FC00:1::32~1128"), ("remove", "Loopback0", "10.1.0.32~132", "FC00:1::33~1128") ]) -def test_syslog_server_tc3_xfail(duthost, op, name, +def test_lo_interface_tc3_xfail(duthost, op, name, dummy_lo_interface_v4, dummy_lo_interface_v6): """ Test expect fail testcase @@ -151,16 +147,50 @@ def test_syslog_server_tc3_xfail(duthost, op, name, delete_tmpfile(duthost, tmpfile) -@pytest.mark.parametrize("op, name, dummy_lo_interface_v4, dummy_lo_interface_v6", [ - ("remove", "Loopback0", "10.1.0.32~132", "FC00:1::32~1128") -]) -def test_syslog_server_tc4_remove(duthost, op, name, - dummy_lo_interface_v4, dummy_lo_interface_v6): - """ Remove v4 and v6 loopback intf +def test_lo_interface_tc4_replace(duthost): + """ Replace v4 and v6 loopback intf ip + Expected output + "LOOPBACK_INTERFACE": { + "Loopback0": {}, + "Loopback0|10.1.0.33/32": {}, + "Loopback0|FC00:1::33/128": {} + } """ json_patch = [ { - "op": "{}".format(op), + "op": "remove", + "path": "/LOOPBACK_INTERFACE/Loopback0|FC00:1::32~1128" + }, + { + "op": "remove", + "path": "/LOOPBACK_INTERFACE/Loopback0|10.1.0.32~132" + }, + { + "op": "add", + "path": "/LOOPBACK_INTERFACE/Loopback0|10.1.0.33~132", + "value": {} + }, + { + "op": "add", + "path": "/LOOPBACK_INTERFACE/Loopback0|FC00:1::33~1128", + "value": {} + } + ] + + tmpfile = generate_tmpfile(duthost) + logger.info("tmpfile {}".format(tmpfile)) + + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + expect_op_success(duthost, output) + + delete_tmpfile(duthost, tmpfile) + +def test_lo_interface_tc5_remove(duthost): + """ Remove v4 and v6 loopback intf config + """ + json_patch = [ + { + "op": "remove", "path": "/LOOPBACK_INTERFACE" } ] From e855c7da85618c54815460c1e59e44bc9f956f37 Mon Sep 17 00:00:00 2001 From: Jingwen Xie Date: Fri, 10 Dec 2021 03:36:38 +0000 Subject: [PATCH 3/7] always remove temp file whether test succeed or fail --- .../test_lo_interface.py | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/tests/generic_config_updater/test_lo_interface.py b/tests/generic_config_updater/test_lo_interface.py index 644e1b27a98..44eed79e617 100644 --- a/tests/generic_config_updater/test_lo_interface.py +++ b/tests/generic_config_updater/test_lo_interface.py @@ -71,10 +71,11 @@ def test_lo_interface_tc1_add_init(duthost, cfg_facts): tmpfile = generate_tmpfile(duthost) logger.info("tmpfile {}".format(tmpfile)) - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) - expect_op_success(duthost, output) - - delete_tmpfile(duthost, tmpfile) + try: + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + expect_op_success(duthost, output) + finally: + delete_tmpfile(duthost, tmpfile) def test_lo_interface_tc2_add_duplicate(duthost): """ Add v4 and v6 duplicate lo intf to config @@ -103,10 +104,11 @@ def test_lo_interface_tc2_add_duplicate(duthost): tmpfile = generate_tmpfile(duthost) logger.info("tmpfile {}".format(tmpfile)) - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) - expect_op_success(duthost, output) - - delete_tmpfile(duthost, tmpfile) + try: + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + expect_op_success(duthost, output) + finally: + delete_tmpfile(duthost, tmpfile) @pytest.mark.parametrize("op, name, dummy_lo_interface_v4, dummy_lo_interface_v6", [ ("add", "Loopback0", "587.1.0.32~132", "FC00:1::32~1128"), @@ -142,10 +144,11 @@ def test_lo_interface_tc3_xfail(duthost, op, name, tmpfile = generate_tmpfile(duthost) logger.info("tmpfile {}".format(tmpfile)) - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) - expect_op_failure(output) - - delete_tmpfile(duthost, tmpfile) + try: + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + expect_op_failure(output) + finally: + delete_tmpfile(duthost, tmpfile) def test_lo_interface_tc4_replace(duthost): """ Replace v4 and v6 loopback intf ip @@ -180,10 +183,11 @@ def test_lo_interface_tc4_replace(duthost): tmpfile = generate_tmpfile(duthost) logger.info("tmpfile {}".format(tmpfile)) - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) - expect_op_success(duthost, output) - - delete_tmpfile(duthost, tmpfile) + try: + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + expect_op_success(duthost, output) + finally: + delete_tmpfile(duthost, tmpfile) def test_lo_interface_tc5_remove(duthost): """ Remove v4 and v6 loopback intf config @@ -198,7 +202,8 @@ def test_lo_interface_tc5_remove(duthost): tmpfile = generate_tmpfile(duthost) logger.info("tmpfile {}".format(tmpfile)) - output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) - expect_op_success(duthost, output) - - delete_tmpfile(duthost, tmpfile) + try: + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + expect_op_success(duthost, output) + finally: + delete_tmpfile(duthost, tmpfile) From 19dd6535e25d722a28b8b599b7bb01a16db729e1 Mon Sep 17 00:00:00 2001 From: Jingwen Xie Date: Mon, 20 Dec 2021 07:17:52 +0000 Subject: [PATCH 4/7] Add rollback instead of config_reload --- tests/generic_config_updater/gu_utils.py | 68 ++++++++++++++++++- .../test_lo_interface.py | 12 ++-- 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/tests/generic_config_updater/gu_utils.py b/tests/generic_config_updater/gu_utils.py index 7c8527b916a..a1318cceafc 100644 --- a/tests/generic_config_updater/gu_utils.py +++ b/tests/generic_config_updater/gu_utils.py @@ -1,12 +1,16 @@ import json import logging +import pytest from tests.common.helpers.assertions import pytest_assert from tests.common.utilities import wait_until - +from tests.common.config_reload import config_reload logger = logging.getLogger(__name__) CONTAINER_SERVICES_LIST = ["swss", "syncd", "radv", "lldp", "dhcp_relay", "teamd", "bgp", "pmon", "telemetry", "acms"] +DEFAULT_CHECKPOINT_NAME = "test" +DEFAULT_ROLLBACK_NAME = DEFAULT_CHECKPOINT_NAME +YANG_IGNORED_OPTIONS = "-i /FEATURE -i /QUEUE -i /SCHEDULER" def generate_tmpfile(duthost): """Generate temp file @@ -28,7 +32,7 @@ def apply_patch(duthost, json_data, dest_file): """ duthost.copy(content=json.dumps(json_data, indent=4), dest=dest_file) - cmds = 'config apply-patch {}'.format(dest_file) + cmds = 'config apply-patch {} {}'.format(YANG_IGNORED_OPTIONS, dest_file) logger.info("Commands: {}".format(cmds)) output = duthost.shell(cmds, module_ignore_errors=True) @@ -143,3 +147,63 @@ def reset_start_limit_hit(duthost, service_name, timeout, interval, delay): reset_service, "Failed to reset service '{}' due to start-limit-hit".format(service_name) ) + +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'], + "Failed to config a checkpoint file: {}".format(cp) + ) + +def delete_checkpoint(duthost, cp=DEFAULT_CHECKPOINT_NAME): + """Run checkpoint on target duthost + + Args: + duthost: Device Under Test (DUT) + cp: checkpoint filename + """ + cmds = 'config delete-checkpoint {}'.format(cp) + + logger.info("Commands: {}".format(cmds)) + output = duthost.shell(cmds, module_ignore_errors=True) + + pytest_assert( + not output['rc'] and "Checkpoint deleted successfully" in output['stdout'], + "Failed to delete a checkpoint file: {}".format(cp) + ) + +def rollback(duthost, rb=DEFAULT_ROLLBACK_NAME): + """Run rollback on target duthost + + Args: + duthost: Device Under Test (DUT) + rb: rollback filename + """ + cmds = 'config rollback {} {}'.format(YANG_IGNORED_OPTIONS, rb) + + logger.info("Commands: {}".format(cmds)) + output = duthost.shell(cmds, module_ignore_errors=True) + + return output + +def rollback_or_reload(duthost): + """Run rollback on target duthost. config_reload if rollback failed. + + Args: + duthost: Device Under Test (DUT) + """ + output = rollback(duthost) + + if output['rc'] or "Config rolled back successfull" not in output['stdout']: + config_reload(duthost) + pytest.fail("config rollback failed. Restored by config_reload") diff --git a/tests/generic_config_updater/test_lo_interface.py b/tests/generic_config_updater/test_lo_interface.py index 44eed79e617..85bceb045ac 100644 --- a/tests/generic_config_updater/test_lo_interface.py +++ b/tests/generic_config_updater/test_lo_interface.py @@ -2,9 +2,9 @@ import pytest from tests.common.helpers.assertions import pytest_assert -from tests.common.config_reload import config_reload from tests.generic_config_updater.gu_utils import apply_patch, expect_op_success, expect_op_failure 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 # Test on t0 topo to verify functionality and to choose predefined variable # "LOOPBACK_INTERFACE": { @@ -27,11 +27,15 @@ def setup_env(duthosts, rand_one_dut_hostname): rand_selected_dut: The fixture returns a randomly selected DuT. """ duthost = duthosts[rand_one_dut_hostname] + create_checkpoint(duthost) yield - logger.info("Restoring config_db.json") - config_reload(duthost) + try: + logger.info("Rolled back to original checkpoint") + rollback_or_reload(duthost) + finally: + delete_checkpoint(duthost) # Cleanup LOOPBACK_INTERFACE config def cleanup_lo_interface_config(duthost, cfg_facts): @@ -66,8 +70,6 @@ def test_lo_interface_tc1_add_init(duthost, cfg_facts): } ] - logger.info("json patch {}".format(json_patch)) - tmpfile = generate_tmpfile(duthost) logger.info("tmpfile {}".format(tmpfile)) From 21621ed16bb36cf7a8f03fc01b9d0195ce1bc505 Mon Sep 17 00:00:00 2001 From: Jingwen Xie Date: Mon, 20 Dec 2021 07:39:57 +0000 Subject: [PATCH 5/7] tune rollback func and remove unnecessary sudo --- tests/generic_config_updater/gu_utils.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/generic_config_updater/gu_utils.py b/tests/generic_config_updater/gu_utils.py index a1318cceafc..699834259ae 100644 --- a/tests/generic_config_updater/gu_utils.py +++ b/tests/generic_config_updater/gu_utils.py @@ -9,7 +9,6 @@ CONTAINER_SERVICES_LIST = ["swss", "syncd", "radv", "lldp", "dhcp_relay", "teamd", "bgp", "pmon", "telemetry", "acms"] DEFAULT_CHECKPOINT_NAME = "test" -DEFAULT_ROLLBACK_NAME = DEFAULT_CHECKPOINT_NAME YANG_IGNORED_OPTIONS = "-i /FEATURE -i /QUEUE -i /SCHEDULER" def generate_tmpfile(duthost): @@ -99,7 +98,7 @@ def start_limit_hit(duthost, service_name): Args: service_name: Service to reset """ - service_status = duthost.shell("sudo systemctl status {}.service | grep 'Active'".format(service_name)) + 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) @@ -123,13 +122,13 @@ def reset_start_limit_hit(duthost, service_name, timeout, interval, delay): """ logger.info("Reset service '{}' due to start-limit-hit".format(service_name)) - service_reset_failed = duthost.shell("sudo systemctl reset-failed {}.service".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("sudo systemctl start {}.service".format(service_name)) + service_start = duthost.shell("systemctl start {}.service".format(service_name)) pytest_assert( not service_start['rc'], "{} systemctl start service fails" @@ -182,27 +181,27 @@ def delete_checkpoint(duthost, cp=DEFAULT_CHECKPOINT_NAME): "Failed to delete a checkpoint file: {}".format(cp) ) -def rollback(duthost, rb=DEFAULT_ROLLBACK_NAME): +def rollback(duthost, cp=DEFAULT_CHECKPOINT_NAME): """Run rollback on target duthost Args: duthost: Device Under Test (DUT) rb: rollback filename """ - cmds = 'config rollback {} {}'.format(YANG_IGNORED_OPTIONS, rb) + cmds = 'config rollback {} {}'.format(YANG_IGNORED_OPTIONS, cp) logger.info("Commands: {}".format(cmds)) output = duthost.shell(cmds, module_ignore_errors=True) return output -def rollback_or_reload(duthost): +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) + output = rollback(duthost, cp) if output['rc'] or "Config rolled back successfull" not in output['stdout']: config_reload(duthost) From 18f390a6cec59e02bdc6e51d5f1a353b0538d3de Mon Sep 17 00:00:00 2001 From: Jingwen Xie Date: Tue, 21 Dec 2021 07:41:47 +0000 Subject: [PATCH 6/7] 1. add checkpoint file existence check 2. add vrf test for lo intf --- tests/generic_config_updater/gu_utils.py | 34 +++- .../test_lo_interface.py | 145 +++++++++++++++++- 2 files changed, 173 insertions(+), 6 deletions(-) diff --git a/tests/generic_config_updater/gu_utils.py b/tests/generic_config_updater/gu_utils.py index 699834259ae..75ae171ec71 100644 --- a/tests/generic_config_updater/gu_utils.py +++ b/tests/generic_config_updater/gu_utils.py @@ -147,6 +147,31 @@ def reset_start_limit_hit(duthost, service_name, timeout, interval, delay): "Failed to reset service '{}' due to start-limit-hit".format(service_name) ) +def list_checkpoints(duthost): + """List checkpoint on target duthost + + Args: + duthost: Device Under Test (DUT) + cp: checkpoint filename + """ + 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 create_checkpoint(duthost, cp=DEFAULT_CHECKPOINT_NAME): """Run checkpoint on target duthost @@ -160,7 +185,9 @@ def create_checkpoint(duthost, cp=DEFAULT_CHECKPOINT_NAME): output = duthost.shell(cmds, module_ignore_errors=True) pytest_assert( - not output['rc'] and "Checkpoint created successfully" in output['stdout'], + not output['rc'] + and "Checkpoint created successfully" in output['stdout'] + and verify_checkpoints_exist(duthost, cp), "Failed to config a checkpoint file: {}".format(cp) ) @@ -171,6 +198,11 @@ def delete_checkpoint(duthost, cp=DEFAULT_CHECKPOINT_NAME): duthost: Device Under Test (DUT) cp: checkpoint filename """ + pytest_assert( + verify_checkpoints_exist(duthost, cp), + "Failed to find the checkpoint file: {}".format(cp) + ) + cmds = 'config delete-checkpoint {}'.format(cp) logger.info("Commands: {}".format(cmds)) diff --git a/tests/generic_config_updater/test_lo_interface.py b/tests/generic_config_updater/test_lo_interface.py index 85bceb045ac..744371baf7d 100644 --- a/tests/generic_config_updater/test_lo_interface.py +++ b/tests/generic_config_updater/test_lo_interface.py @@ -2,16 +2,22 @@ import pytest from tests.common.helpers.assertions import pytest_assert -from tests.generic_config_updater.gu_utils import apply_patch, expect_op_success, expect_op_failure +from tests.generic_config_updater.gu_utils import apply_patch, expect_op_success, expect_op_failure, expect_res_success 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 # Test on t0 topo to verify functionality and to choose predefined variable # "LOOPBACK_INTERFACE": { - # "Loopback0": {}, - # "Loopback0|10.1.0.32/32": {}, - # "Loopback0|FC00:1::32/128": {} +# "Loopback0": {}, +# "Loopback0|10.1.0.32/32": {}, +# "Loopback0|FC00:1::32/128": {} # } +# admin@vlab-01:~$ show ip interfaces | grep Loopback0 +# Loopback0 10.1.0.32/32 up/up N/A N/A +# admin@vlab-01:~$ show ipv6 interfaces | grep Loopback0 +# Loopback0 fc00:1::32/128 up/up N/A N/A +# fe80::4a3:18ff:fec2:f9e3%Loopback0/64 N/A N/A + pytestmark = [ pytest.mark.topology('t0'), ] @@ -21,7 +27,9 @@ @pytest.fixture(autouse=True) def setup_env(duthosts, rand_one_dut_hostname): """ - Setup/teardown fixture for each loopback interface test + Setup/teardown fixture for each loopback interface test. + rollback to check if it goes back to starting config without vrf set + Args: duthosts: list of DUTs. rand_selected_dut: The fixture returns a randomly selected DuT. @@ -34,6 +42,8 @@ def setup_env(duthosts, rand_one_dut_hostname): try: logger.info("Rolled back to original checkpoint") rollback_or_reload(duthost) + check_show_lo_intf(duthost, "Loopback0", ["10.1.0.32/32"], ["Vrf"], ipv4=True) + check_show_lo_intf(duthost, "Loopback0", ["fc00:1::32/128"], ["Vrf"], ipv4=False) finally: delete_checkpoint(duthost) @@ -46,6 +56,21 @@ def cleanup_lo_interface_config(duthost, cfg_facts): pytest_assert(not del_loopback_interface['rc'], "Loopback interface '{}' is not deleted successfully".format(lo_interface)) +def check_show_lo_intf(duthost, lo_intf_name, expected_content_list, unexpected_content_list, ipv4=True): + """Check lo interface status by show command + + Sample output: + admin@vlab-01:~$ show ip interfaces | grep Loopback0 + Loopback0 10.1.0.32/32 up/up N/A N/A + admin@vlab-01:~$ show ipv6 interfaces | grep Loopback0 + Loopback0 fc00:1::32/128 up/up N/A N/A + fe80::4a3:18ff:fec2:f9e3%Loopback0/64 N/A N/A + """ + address_family = "ip" if ipv4 else "ipv6" + output = duthost.shell("show {} interfaces | grep {} || true".format(address_family, lo_intf_name)) + + expect_res_success(duthost, output, expected_content_list, unexpected_content_list) + def test_lo_interface_tc1_add_init(duthost, cfg_facts): """ Clean up orig lo interface and test initial addion of v4 and v6 lo intf @@ -76,6 +101,10 @@ def test_lo_interface_tc1_add_init(duthost, cfg_facts): try: output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) expect_op_success(duthost, output) + + check_show_lo_intf(duthost, "Loopback0", ["10.1.0.32/32"], [], ipv4=True) + check_show_lo_intf(duthost, "Loopback0", ["fc00:1::32/128"], [], ipv4=False) + finally: delete_tmpfile(duthost, tmpfile) @@ -109,6 +138,9 @@ def test_lo_interface_tc2_add_duplicate(duthost): try: output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) expect_op_success(duthost, output) + + check_show_lo_intf(duthost, "Loopback0", ["10.1.0.32/32"], [], ipv4=True) + check_show_lo_intf(duthost, "Loopback0", ["fc00:1::32/128"], [], ipv4=False) finally: delete_tmpfile(duthost, tmpfile) @@ -160,6 +192,11 @@ def test_lo_interface_tc4_replace(duthost): "Loopback0|10.1.0.33/32": {}, "Loopback0|FC00:1::33/128": {} } + admin@vlab-01:~$ show ip interfaces | grep Loopback0 + Loopback0 10.1.0.33/32 up/up N/A N/A + admin@vlab-01:~$ show ipv6 interfaces | grep Loopback0 + Loopback0 fc00:1::33/128 up/up N/A N/A + fe80::a8cb:e8ff:fe6e:df6e%Loopback0/64 N/A N/A """ json_patch = [ { @@ -188,6 +225,9 @@ def test_lo_interface_tc4_replace(duthost): try: output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) expect_op_success(duthost, output) + + check_show_lo_intf(duthost, "Loopback0", ["10.1.0.33/32"], ["10.1.0.32/32"], ipv4=True) + check_show_lo_intf(duthost, "Loopback0", ["fc00:1::33/128"], ["fc00:1::32/128"], ipv4=False) finally: delete_tmpfile(duthost, tmpfile) @@ -207,5 +247,100 @@ def test_lo_interface_tc5_remove(duthost): try: output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) expect_op_success(duthost, output) + + check_show_lo_intf(duthost, "Loopback0", [], ["10.1.0.32/32"], ipv4=True) + check_show_lo_intf(duthost, "Loopback0", [], ["fc00:1::32/128"], ipv4=False) + finally: + delete_tmpfile(duthost, tmpfile) + +def check_vrf_route_for_lo_intf(duthost, vrf_name, lo_intf_name, ipv4=True): + """Check ip route for specific vrf + + Sample output: + admin@vlab-01:~$ show ip route vrf Vrf_01 | grep Loopback0 + C>* 10.1.0.32/32 is directly connected, Loopback0, 00:00:13 + """ + address_family = "ip" if ipv4 else "ipv6" + output = duthost.shell("show {} route vrf {} | grep {}".format(address_family, vrf_name, lo_intf_name)) + + logger.info("get_vrf_route_for_lo_intf output {}".format(output)) + pytest_assert(not output['rc'], + "Route not found for {} in vrf {}".format(lo_intf_name, vrf_name)) + +def setup_vrf_config(duthost): + """Create two vrf and bind Loopback0 to Vrf_01 + + admin@vlab-01:~$ show ip interfaces | grep Loopback0 + Loopback0 Vrf_01 10.1.0.32/32 up/up N/A N/A + admin@vlab-01:~$ show ipv6 interfaces | grep Loopback0 + Loopback0 Vrf_01 fc00:1::32/128 up/up N/A N/A + fe80::a8cb:e8ff:fe6e:df6e%Loopback0/64 N/A N/A + admin@vlab-01:~$ show ip route vrf Vrf_01 + VRF Vrf_01: + C>* 10.1.0.32/32 is directly connected, Loopback0, 00:00:13 + + """ + cmds = [] + cmds.append("config vrf add Vrf_01") + cmds.append("config vrf add Vrf_02") + + output = duthost.shell_cmds(cmds=cmds) + + json_patch = [ + { + "op": "add", + "path": "/LOOPBACK_INTERFACE/Loopback0/vrf_name", + "value": "Vrf_01" + } + ] + + tmpfile = generate_tmpfile(duthost) + logger.info("tmpfile {}".format(tmpfile)) + + try: + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + expect_op_success(duthost, output) + + check_show_lo_intf(duthost, "Loopback0", ["10.1.0.32/32", "Vrf_01"], [], ipv4=True) + check_show_lo_intf(duthost, "Loopback0", ["fc00:1::32/128", "Vrf_01"], [], ipv4=False) + + check_vrf_route_for_lo_intf(duthost, "Vrf_01", "Loopback0", ipv4=True) + check_vrf_route_for_lo_intf(duthost, "Vrf_01", "Loopback0", ipv4=False) + finally: + delete_tmpfile(duthost, tmpfile) + +def test_lo_interface_tc6_vrf_change(duthost): + """ Replace lo interface vrf + + admin@vlab-01:~$ show ip interfaces | grep Loopback0 + Loopback0 Vrf_02 10.1.0.32/32 up/up N/A N/A + admin@vlab-01:~$ show ipv6 interfaces | grep Loopback0 + Loopback0 Vrf_02 fc00:1::32/128 up/up N/A N/A + fe80::a8cb:e8ff:fe6e:df6e%Loopback0/64 N/A N/A + admin@vlab-01:~$ show ip route vrf Vrf_02 + VRF Vrf_02: + C>* 10.1.0.32/32 is directly connected, Loopback0, 00:00:17 + """ + setup_vrf_config(duthost) + json_patch = [ + { + "op": "replace", + "path": "/LOOPBACK_INTERFACE/Loopback0/vrf_name", + "value": "Vrf_02" + } + ] + + tmpfile = generate_tmpfile(duthost) + logger.info("tmpfile {}".format(tmpfile)) + + try: + output = apply_patch(duthost, json_data=json_patch, dest_file=tmpfile) + expect_op_success(duthost, output) + + check_show_lo_intf(duthost, "Loopback0", ["10.1.0.32/32", "Vrf_02"], [], ipv4=True) + check_show_lo_intf(duthost, "Loopback0", ["fc00:1::32/128", "Vrf_02"], [], ipv4=False) + + check_vrf_route_for_lo_intf(duthost, "Vrf_02", "Loopback0", ipv4=True) + check_vrf_route_for_lo_intf(duthost, "Vrf_02", "Loopback0", ipv4=False) finally: delete_tmpfile(duthost, tmpfile) From 96fe6b8015e811305616cc1fbd254cf940df9d50 Mon Sep 17 00:00:00 2001 From: Jingwen Xie Date: Tue, 21 Dec 2021 07:58:37 +0000 Subject: [PATCH 7/7] remove logging info --- tests/generic_config_updater/test_lo_interface.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/generic_config_updater/test_lo_interface.py b/tests/generic_config_updater/test_lo_interface.py index 744371baf7d..9eb8287e29c 100644 --- a/tests/generic_config_updater/test_lo_interface.py +++ b/tests/generic_config_updater/test_lo_interface.py @@ -263,7 +263,6 @@ def check_vrf_route_for_lo_intf(duthost, vrf_name, lo_intf_name, ipv4=True): address_family = "ip" if ipv4 else "ipv6" output = duthost.shell("show {} route vrf {} | grep {}".format(address_family, vrf_name, lo_intf_name)) - logger.info("get_vrf_route_for_lo_intf output {}".format(output)) pytest_assert(not output['rc'], "Route not found for {} in vrf {}".format(lo_intf_name, vrf_name))