From 8ad800685e69bbfb4e10c827cdde8168b4704342 Mon Sep 17 00:00:00 2001 From: hdwhdw Date: Fri, 17 Jan 2025 18:09:40 +0000 Subject: [PATCH 1/6] initial commit. --- tests/gnmi/test_gnoi_system.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/gnmi/test_gnoi_system.py b/tests/gnmi/test_gnoi_system.py index a4ed6355369..84f08bd568d 100644 --- a/tests/gnmi/test_gnoi_system.py +++ b/tests/gnmi/test_gnoi_system.py @@ -35,6 +35,18 @@ def test_gnoi_system_time(duthosts, rand_one_dut_hostname, localhost): pytest_assert("time" in msg_json, "System.Time API did not return time") +def test_gnoi_system_reboot(duthosts, rand_one_dut_hostname, localhost): + """ + Verify the gNOI System Reboot API triggers a reboot and the device comes back online. + """ + duthost = duthosts[rand_one_dut_hostname] + + # Trigger reboot + ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1}') + pytest_assert(ret == 0, "System.Reboot API reported failure (rc = {}) with message: {}".format(ret, msg)) + logging.info("System.Reboot API returned msg: {}".format(msg)) + + def extract_first_json_substring(s): """ Extract the first JSON substring from a given string. From 8399c65c9586bfe73a749427458e8cb8af9bd758 Mon Sep 17 00:00:00 2001 From: hdwhdw Date: Wed, 29 Jan 2025 22:56:44 +0000 Subject: [PATCH 2/6] add a testcase that immediately request status after reboot. --- tests/gnmi/test_gnoi_system.py | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/gnmi/test_gnoi_system.py b/tests/gnmi/test_gnoi_system.py index 84f08bd568d..366b2bd448f 100644 --- a/tests/gnmi/test_gnoi_system.py +++ b/tests/gnmi/test_gnoi_system.py @@ -47,6 +47,43 @@ def test_gnoi_system_reboot(duthosts, rand_one_dut_hostname, localhost): logging.info("System.Reboot API returned msg: {}".format(msg)) +def test_gnoi_system_reboot_fail_invalid_method(duthosts, rand_one_dut_hostname, localhost): + """ + Verify the gNOI System Reboot API fails with invalid method. + """ + duthost = duthosts[rand_one_dut_hostname] + + # Trigger reboot with invalid method + ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 2}') + pytest_assert(ret != 0, "System.Reboot API did not report failure with invalid method") + + +def test_gnoi_system_reboot_status_immediately(duthosts, rand_one_dut_hostname, localhost): + """ + Verify the gNOI System RebootStatus API returns the correct status immediately after reboot. + """ + duthost = duthosts[rand_one_dut_hostname] + + # Trigger reboot + ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1, "message": "test"}') + pytest_assert(ret == 0, "System.Reboot API reported failure (rc = {}) with message: {}".format(ret, msg)) + logging.info("System.Reboot API returned msg: {}".format(msg)) + + # Get reboot status + ret, msg = gnoi_request(duthost, localhost, "RebootStatus", "") + pytest_assert(ret == 0, "System.RebootStatus API reported failure (rc = {}) with message: {}".format(ret, msg)) + logging.info("System.RebootStatus API returned msg: {}".format(msg)) + # Message should contain a json substring like this + # {"active":true,"wait":0,"when":0,"reason":"test","count":1,"method":1,"status":1} + # Extract JSON part from the message + msg_json = extract_first_json_substring(msg) + if not msg_json: + pytest.fail("Failed to extract JSON from System.RebootStatus API response") + logging.info("Extracted JSON: {}".format(msg_json)) + pytest_assert("active" in msg_json, "System.RebootStatus API did not return active") + pytest_assert(msg_json["active"] is True, "System.RebootStatus API did not return active = true") + + def extract_first_json_substring(s): """ Extract the first JSON substring from a given string. From 1a96104563e047ab9bfa6138c0337db6d9504bc7 Mon Sep 17 00:00:00 2001 From: hdwhdw Date: Thu, 30 Jan 2025 21:04:05 +0000 Subject: [PATCH 3/6] update invalid method. --- tests/gnmi/test_gnoi_system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gnmi/test_gnoi_system.py b/tests/gnmi/test_gnoi_system.py index 366b2bd448f..103c8f41412 100644 --- a/tests/gnmi/test_gnoi_system.py +++ b/tests/gnmi/test_gnoi_system.py @@ -54,7 +54,7 @@ def test_gnoi_system_reboot_fail_invalid_method(duthosts, rand_one_dut_hostname, duthost = duthosts[rand_one_dut_hostname] # Trigger reboot with invalid method - ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 2}') + ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 99}') pytest_assert(ret != 0, "System.Reboot API did not report failure with invalid method") From a2724b7288f7f54dcb6e2c88afff0260abf07c9e Mon Sep 17 00:00:00 2001 From: hdwhdw Date: Thu, 30 Jan 2025 21:49:21 +0000 Subject: [PATCH 4/6] gnoi_system_reboot_status_after_startup --- tests/gnmi/test_gnoi_system.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/gnmi/test_gnoi_system.py b/tests/gnmi/test_gnoi_system.py index 103c8f41412..a9177d1c3d4 100644 --- a/tests/gnmi/test_gnoi_system.py +++ b/tests/gnmi/test_gnoi_system.py @@ -4,6 +4,7 @@ from .helper import gnoi_request from tests.common.helpers.assertions import pytest_assert +from tests.common.reboot import wait_for_startup import re pytestmark = [ @@ -84,6 +85,35 @@ def test_gnoi_system_reboot_status_immediately(duthosts, rand_one_dut_hostname, pytest_assert(msg_json["active"] is True, "System.RebootStatus API did not return active = true") +def gnoi_system_reboot_status_after_startup(duthosts, rand_one_dut_hostname, localhost): + """ + Verify the gNOI System RebootStatus API returns the correct status after the device has started up. + """ + duthost = duthosts[rand_one_dut_hostname] + + # Trigger reboot + ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1, "message": "test"}') + pytest_assert(ret == 0, "System.Reboot API reported failure (rc = {}) with message: {}".format(ret, msg)) + logging.info("System.Reboot API returned msg: {}".format(msg)) + + # Wait for device to come back online + wait_for_startup(duthost) + + # Get reboot status + ret, msg = gnoi_request(duthost, localhost, "RebootStatus", "") + pytest_assert(ret == 0, "System.RebootStatus API reported failure (rc = {}) with message: {}".format(ret, msg)) + logging.info("System.RebootStatus API returned msg: {}".format(msg)) + # Message should contain a json substring like this + # {"active":false,"wait":0,"when":0,"reason":"test","count":1,"method":1,"status":1} + # Extract JSON part from the message + msg_json = extract_first_json_substring(msg) + if not msg_json: + pytest.fail("Failed to extract JSON from System.RebootStatus API response") + logging.info("Extracted JSON: {}".format(msg_json)) + pytest_assert("active" in msg_json, "System.RebootStatus API did not return active") + pytest_assert(msg_json["active"] is False, "System.RebootStatus API did not return active = false") + + def extract_first_json_substring(s): """ Extract the first JSON substring from a given string. From 9fcacf49ccb750f25a1c6410463d54569847552f Mon Sep 17 00:00:00 2001 From: hdwhdw Date: Thu, 30 Jan 2025 21:51:14 +0000 Subject: [PATCH 5/6] test_gnoi_system_reboot_when_reboot_active --- tests/gnmi/test_gnoi_system.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/gnmi/test_gnoi_system.py b/tests/gnmi/test_gnoi_system.py index a9177d1c3d4..e19b5dd1645 100644 --- a/tests/gnmi/test_gnoi_system.py +++ b/tests/gnmi/test_gnoi_system.py @@ -59,6 +59,22 @@ def test_gnoi_system_reboot_fail_invalid_method(duthosts, rand_one_dut_hostname, pytest_assert(ret != 0, "System.Reboot API did not report failure with invalid method") +def test_gnoi_system_reboot_when_reboot_active(duthosts, rand_one_dut_hostname, localhost): + """ + Verify the gNOI System Reboot API fails if a reboot is already active. + """ + duthost = duthosts[rand_one_dut_hostname] + + # Trigger first reboot + ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1}') + pytest_assert(ret == 0, "System.Reboot API reported failure (rc = {}) with message: {}".format(ret, msg)) + logging.info("System.Reboot API returned msg: {}".format(msg)) + + # Trigger second reboot while the first one is still active + ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1}') + pytest_assert(ret != 0, "System.Reboot API did not report failure when reboot is already active") + + def test_gnoi_system_reboot_status_immediately(duthosts, rand_one_dut_hostname, localhost): """ Verify the gNOI System RebootStatus API returns the correct status immediately after reboot. From f0063b816726c446e5e1e70469d9031b5d98d12f Mon Sep 17 00:00:00 2001 From: Kanchana Velusamy Date: Thu, 30 Jan 2025 09:06:09 +0000 Subject: [PATCH 6/6] Skip GNMI check during teardown and disable LogAnalyzer for reboot tests --- tests/gnmi/conftest.py | 8 +++++- tests/gnmi/test_gnoi_system.py | 51 ++++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/tests/gnmi/conftest.py b/tests/gnmi/conftest.py index 6a74fc580b9..859ba20aa6f 100644 --- a/tests/gnmi/conftest.py +++ b/tests/gnmi/conftest.py @@ -210,7 +210,13 @@ def setup_gnmi_server(duthosts, rand_one_dut_hostname, localhost, ptfhost): # Rollback configuration rollback(duthost, SETUP_ENV_CP) - recover_cert_config(duthost) + # Get the skip_gnmi_check flag from duthost options + skip_gnmi_check = duthost.host.options.get('skip_gnmi_check', False) + # Skip GNMI restart if the reboot flag is set + if not skip_gnmi_check: + recover_cert_config(duthost) + else: + logging.info("Skipping GNMI restart due to skip_gnmi_check flag") @pytest.fixture(scope="module", autouse=True) diff --git a/tests/gnmi/test_gnoi_system.py b/tests/gnmi/test_gnoi_system.py index e19b5dd1645..aa775ea9857 100644 --- a/tests/gnmi/test_gnoi_system.py +++ b/tests/gnmi/test_gnoi_system.py @@ -11,6 +11,7 @@ pytest.mark.topology('any') ] +MAX_TIME_TO_REBOOT = 300 """ This module contains tests for the gNOI System API. @@ -42,45 +43,58 @@ def test_gnoi_system_reboot(duthosts, rand_one_dut_hostname, localhost): """ duthost = duthosts[rand_one_dut_hostname] + # Set flag to indicate that this test involves reboot + duthost.host.options['skip_gnmi_check'] = True + # Trigger reboot - ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1}') + ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1,"delay":0,"message":"Cold Reboot"}') pytest_assert(ret == 0, "System.Reboot API reported failure (rc = {}) with message: {}".format(ret, msg)) logging.info("System.Reboot API returned msg: {}".format(msg)) - +@pytest.mark.disable_loganalyzer def test_gnoi_system_reboot_fail_invalid_method(duthosts, rand_one_dut_hostname, localhost): """ Verify the gNOI System Reboot API fails with invalid method. """ duthost = duthosts[rand_one_dut_hostname] + # Set flag to indicate that this test involves reboot + duthost.host.options['skip_gnmi_check'] = True + # Trigger reboot with invalid method ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 99}') pytest_assert(ret != 0, "System.Reboot API did not report failure with invalid method") - +@pytest.mark.disable_loganalyzer def test_gnoi_system_reboot_when_reboot_active(duthosts, rand_one_dut_hostname, localhost): """ Verify the gNOI System Reboot API fails if a reboot is already active. """ duthost = duthosts[rand_one_dut_hostname] + # Set flag to indicate that this test involves reboot + duthost.host.options['skip_gnmi_check'] = True + # Trigger first reboot - ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1}') + ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1,"delay":0,"message":"Cold Reboot"}') pytest_assert(ret == 0, "System.Reboot API reported failure (rc = {}) with message: {}".format(ret, msg)) logging.info("System.Reboot API returned msg: {}".format(msg)) # Trigger second reboot while the first one is still active - ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1}') + ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1,"delay":0,"message":"Cold Reboot"}') pytest_assert(ret != 0, "System.Reboot API did not report failure when reboot is already active") +@pytest.mark.disable_loganalyzer def test_gnoi_system_reboot_status_immediately(duthosts, rand_one_dut_hostname, localhost): """ Verify the gNOI System RebootStatus API returns the correct status immediately after reboot. """ duthost = duthosts[rand_one_dut_hostname] + # Set flag to indicate that this test involves reboot + duthost.host.options['skip_gnmi_check'] = True + # Trigger reboot ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1, "message": "test"}') pytest_assert(ret == 0, "System.Reboot API reported failure (rc = {}) with message: {}".format(ret, msg)) @@ -107,6 +121,9 @@ def gnoi_system_reboot_status_after_startup(duthosts, rand_one_dut_hostname, loc """ duthost = duthosts[rand_one_dut_hostname] + # Set flag to indicate that this test involves reboot + duthost.host.options['skip_gnmi_check'] = True + # Trigger reboot ret, msg = gnoi_request(duthost, localhost, "Reboot", '{"method": 1, "message": "test"}') pytest_assert(ret == 0, "System.Reboot API reported failure (rc = {}) with message: {}".format(ret, msg)) @@ -138,12 +155,18 @@ def extract_first_json_substring(s): :return: The first JSON substring if found, otherwise None. """ - json_pattern = re.compile(r'\{.*?\}') - match = json_pattern.search(s) - if match: - try: - return json.loads(match.group()) - except json.JSONDecodeError: - logging.error("Failed to parse JSON: {}".format(match.group())) - return None - return None + start_index = s.find('{') # Find the first '{' in the string + if start_index == -1: + logging.error("No JSON found in response: {}".format(s)) + return None + json_str = s[start_index:] # Extract substring starting from '{' + try: + parsed_json = json.loads(json_str) # Attempt to parse the JSON + # Handle cases where "status": {} is empty + if "status" in parsed_json and parsed_json["status"] == {}: + logging.warning("Replacing empty 'status' field with a default value.") + parsed_json["status"] = {"unknown": "empty_status"} + return parsed_json + except json.JSONDecodeError as e: + logging.error("Failed to parse JSON: {} | Error: {}".format(json_str, e)) + return None