44
55from .helper import gnoi_request
66from tests .common .helpers .assertions import pytest_assert
7+ from tests .common .reboot import wait_for_startup
78import re
89
910pytestmark = [
1011 pytest .mark .topology ('any' )
1112]
1213
14+ MAX_TIME_TO_REBOOT = 300
1315
1416"""
1517This module contains tests for the gNOI System API.
@@ -45,28 +47,56 @@ def test_gnoi_system_reboot(duthosts, rand_one_dut_hostname, localhost):
4547 duthost .host .options ['skip_gnmi_check' ] = True
4648
4749 # Trigger reboot
48- ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 1}' )
50+ ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 1,"delay":0,"message":"Cold Reboot" }' )
4951 pytest_assert (ret == 0 , "System.Reboot API reported failure (rc = {}) with message: {}" .format (ret , msg ))
5052 logging .info ("System.Reboot API returned msg: {}" .format (msg ))
5153
5254
55+ @pytest .mark .disable_loganalyzer
5356def test_gnoi_system_reboot_fail_invalid_method (duthosts , rand_one_dut_hostname , localhost ):
5457 """
5558 Verify the gNOI System Reboot API fails with invalid method.
5659 """
5760 duthost = duthosts [rand_one_dut_hostname ]
5861
62+ # Set flag to indicate that this test involves reboot
63+ duthost .host .options ['skip_gnmi_check' ] = True
64+
5965 # Trigger reboot with invalid method
60- ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 2 }' )
66+ ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 99 }' )
6167 pytest_assert (ret != 0 , "System.Reboot API did not report failure with invalid method" )
6268
6369
70+ @pytest .mark .disable_loganalyzer
71+ def test_gnoi_system_reboot_when_reboot_active (duthosts , rand_one_dut_hostname , localhost ):
72+ """
73+ Verify the gNOI System Reboot API fails if a reboot is already active.
74+ """
75+ duthost = duthosts [rand_one_dut_hostname ]
76+
77+ # Set flag to indicate that this test involves reboot
78+ duthost .host .options ['skip_gnmi_check' ] = True
79+
80+ # Trigger first reboot
81+ ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 1,"delay":0,"message":"Cold Reboot"}' )
82+ pytest_assert (ret == 0 , "System.Reboot API reported failure (rc = {}) with message: {}" .format (ret , msg ))
83+ logging .info ("System.Reboot API returned msg: {}" .format (msg ))
84+
85+ # Trigger second reboot while the first one is still active
86+ ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 1,"delay":0,"message":"Cold Reboot"}' )
87+ pytest_assert (ret != 0 , "System.Reboot API did not report failure when reboot is already active" )
88+
89+
90+ @pytest .mark .disable_loganalyzer
6491def test_gnoi_system_reboot_status_immediately (duthosts , rand_one_dut_hostname , localhost ):
6592 """
6693 Verify the gNOI System RebootStatus API returns the correct status immediately after reboot.
6794 """
6895 duthost = duthosts [rand_one_dut_hostname ]
6996
97+ # Set flag to indicate that this test involves reboot
98+ duthost .host .options ['skip_gnmi_check' ] = True
99+
70100 # Trigger reboot
71101 ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 1, "message": "test"}' )
72102 pytest_assert (ret == 0 , "System.Reboot API reported failure (rc = {}) with message: {}" .format (ret , msg ))
@@ -87,6 +117,37 @@ def test_gnoi_system_reboot_status_immediately(duthosts, rand_one_dut_hostname,
87117 pytest_assert (msg_json ["active" ] is True , "System.RebootStatus API did not return active = true" )
88118
89119
120+ def gnoi_system_reboot_status_after_startup (duthosts , rand_one_dut_hostname , localhost ):
121+ """
122+ Verify the gNOI System RebootStatus API returns the correct status after the device has started up.
123+ """
124+ duthost = duthosts [rand_one_dut_hostname ]
125+
126+ # Set flag to indicate that this test involves reboot
127+ duthost .host .options ['skip_gnmi_check' ] = True
128+
129+ # Trigger reboot
130+ ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 1, "message": "test"}' )
131+ pytest_assert (ret == 0 , "System.Reboot API reported failure (rc = {}) with message: {}" .format (ret , msg ))
132+ logging .info ("System.Reboot API returned msg: {}" .format (msg ))
133+
134+ # Wait for device to come back online
135+ wait_for_startup (duthost , localhost , 0 , MAX_TIME_TO_REBOOT )
136+
137+ # Get reboot status
138+ ret , msg = gnoi_request (duthost , localhost , "RebootStatus" , "" )
139+ pytest_assert (ret == 0 , "System.RebootStatus API reported failure (rc = {}) with message: {}" .format (ret , msg ))
140+ logging .info ("System.RebootStatus API returned msg: {}" .format (msg ))
141+ # Message should contain a json substring like this
142+ # {"active":false,"wait":0,"when":0,"reason":"test","count":1,"method":1,"status":1}
143+ # Extract JSON part from the message
144+ msg_json = extract_first_json_substring (msg )
145+ if not msg_json :
146+ pytest .fail ("Failed to extract JSON from System.RebootStatus API response" )
147+ logging .info ("Extracted JSON: {}" .format (msg_json ))
148+ pytest_assert ("active" in msg_json , "System.RebootStatus API did not return active" )
149+ pytest_assert (msg_json ["active" ] is False , "System.RebootStatus API did not return active = false" )
150+
90151def extract_first_json_substring (s ):
91152 """
92153 Extract the first JSON substring from a given string.
@@ -95,12 +156,21 @@ def extract_first_json_substring(s):
95156 :return: The first JSON substring if found, otherwise None.
96157 """
97158
98- json_pattern = re .compile (r'\{.*?\}' )
99- match = json_pattern .search (s )
100- if match :
101- try :
102- return json .loads (match .group ())
103- except json .JSONDecodeError :
104- logging .error ("Failed to parse JSON: {}" .format (match .group ()))
105- return None
106- return None
159+ start_index = s .find ('{' ) # Find the first '{' in the string
160+ if start_index == - 1 :
161+ logging .error ("No JSON found in response: {}" .format (s ))
162+ return None
163+
164+ json_str = s [start_index :] # Extract substring starting from '{'
165+ try :
166+ parsed_json = json .loads (json_str ) # Attempt to parse the JSON
167+ # Handle cases where "status": {} is empty
168+ if "status" in parsed_json and parsed_json ["status" ] == {}:
169+ logging .warning ("Replacing empty 'status' field with a default value." )
170+ parsed_json ["status" ] = {"unknown" : "empty_status" }
171+ return parsed_json
172+
173+ except json .JSONDecodeError as e :
174+ logging .error ("Failed to parse JSON: {} | Error: {}" .format (json_str , e ))
175+ return None
176+
0 commit comments