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,7 +47,7 @@ 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
@@ -56,17 +58,42 @@ def test_gnoi_system_reboot_fail_invalid_method(duthosts, rand_one_dut_hostname,
5658 """
5759 duthost = duthosts [rand_one_dut_hostname ]
5860
61+ # Set flag to indicate that this test involves reboot
62+ duthost .host .options ['skip_gnmi_check' ] = True
63+
5964 # Trigger reboot with invalid method
60- ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 2 }' )
65+ ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 99 }' )
6166 pytest_assert (ret != 0 , "System.Reboot API did not report failure with invalid method" )
6267
6368
69+ def test_gnoi_system_reboot_when_reboot_active (duthosts , rand_one_dut_hostname , localhost ):
70+ """
71+ Verify the gNOI System Reboot API fails if a reboot is already active.
72+ """
73+ duthost = duthosts [rand_one_dut_hostname ]
74+
75+ # Set flag to indicate that this test involves reboot
76+ duthost .host .options ['skip_gnmi_check' ] = True
77+
78+ # Trigger first reboot
79+ ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 1,"delay":0,"message":"Cold Reboot"}' )
80+ pytest_assert (ret == 0 , "System.Reboot API reported failure (rc = {}) with message: {}" .format (ret , msg ))
81+ logging .info ("System.Reboot API returned msg: {}" .format (msg ))
82+
83+ # Trigger second reboot while the first one is still active
84+ ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 1,"delay":0,"message":"Cold Reboot"}' )
85+ pytest_assert (ret != 0 , "System.Reboot API did not report failure when reboot is already active" )
86+
87+
6488def test_gnoi_system_reboot_status_immediately (duthosts , rand_one_dut_hostname , localhost ):
6589 """
6690 Verify the gNOI System RebootStatus API returns the correct status immediately after reboot.
6791 """
6892 duthost = duthosts [rand_one_dut_hostname ]
6993
94+ # Set flag to indicate that this test involves reboot
95+ duthost .host .options ['skip_gnmi_check' ] = True
96+
7097 # Trigger reboot
7198 ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 1, "message": "test"}' )
7299 pytest_assert (ret == 0 , "System.Reboot API reported failure (rc = {}) with message: {}" .format (ret , msg ))
@@ -87,6 +114,37 @@ def test_gnoi_system_reboot_status_immediately(duthosts, rand_one_dut_hostname,
87114 pytest_assert (msg_json ["active" ] is True , "System.RebootStatus API did not return active = true" )
88115
89116
117+ def gnoi_system_reboot_status_after_startup (duthosts , rand_one_dut_hostname , localhost ):
118+ """
119+ Verify the gNOI System RebootStatus API returns the correct status after the device has started up.
120+ """
121+ duthost = duthosts [rand_one_dut_hostname ]
122+
123+ # Set flag to indicate that this test involves reboot
124+ duthost .host .options ['skip_gnmi_check' ] = True
125+
126+ # Trigger reboot
127+ ret , msg = gnoi_request (duthost , localhost , "Reboot" , '{"method": 1, "message": "test"}' )
128+ pytest_assert (ret == 0 , "System.Reboot API reported failure (rc = {}) with message: {}" .format (ret , msg ))
129+ logging .info ("System.Reboot API returned msg: {}" .format (msg ))
130+
131+ # Wait for device to come back online
132+ wait_for_startup (duthost , localhost , 0 , MAX_TIME_TO_REBOOT )
133+
134+ # Get reboot status
135+ ret , msg = gnoi_request (duthost , localhost , "RebootStatus" , "" )
136+ pytest_assert (ret == 0 , "System.RebootStatus API reported failure (rc = {}) with message: {}" .format (ret , msg ))
137+ logging .info ("System.RebootStatus API returned msg: {}" .format (msg ))
138+ # Message should contain a json substring like this
139+ # {"active":false,"wait":0,"when":0,"reason":"test","count":1,"method":1,"status":1}
140+ # Extract JSON part from the message
141+ msg_json = extract_first_json_substring (msg )
142+ if not msg_json :
143+ pytest .fail ("Failed to extract JSON from System.RebootStatus API response" )
144+ logging .info ("Extracted JSON: {}" .format (msg_json ))
145+ pytest_assert ("active" in msg_json , "System.RebootStatus API did not return active" )
146+ pytest_assert (msg_json ["active" ] is False , "System.RebootStatus API did not return active = false" )
147+
90148def extract_first_json_substring (s ):
91149 """
92150 Extract the first JSON substring from a given string.
@@ -95,12 +153,21 @@ def extract_first_json_substring(s):
95153 :return: The first JSON substring if found, otherwise None.
96154 """
97155
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
156+ start_index = s .find ('{' ) # Find the first '{' in the string
157+ if start_index == - 1 :
158+ logging .error ("No JSON found in response: {}" .format (s ))
159+ return None
160+
161+ json_str = s [start_index :] # Extract substring starting from '{'
162+ try :
163+ parsed_json = json .loads (json_str ) # Attempt to parse the JSON
164+ # Handle cases where "status": {} is empty
165+ if "status" in parsed_json and parsed_json ["status" ] == {}:
166+ logging .warning ("Replacing empty 'status' field with a default value." )
167+ parsed_json ["status" ] = {"unknown" : "empty_status" }
168+ return parsed_json
169+
170+ except json .JSONDecodeError as e :
171+ logging .error ("Failed to parse JSON: {} | Error: {}" .format (json_str , e ))
172+ return None
173+
0 commit comments