Skip to content

Commit c59e254

Browse files
rkavitha-hclbibhuprasad-hcl
authored andcommitted
sonic-host-services changes for gNOI Cold Reboot
1 parent d5a250e commit c59e254

File tree

5 files changed

+411
-2
lines changed

5 files changed

+411
-2
lines changed

host_modules/gnoi_reboot.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
"""gNOI reboot module which performs reboot"""
2+
3+
import json
4+
import logging
5+
import threading
6+
import time
7+
from host_modules import host_service
8+
from utils.run_cmd import _run_command
9+
10+
MOD_NAME = 'gnoi_reboot'
11+
# Reboot method in reboot request
12+
# Both enum and string representations are supported
13+
REBOOTMETHOD_COLD_BOOT_VALUES = {1, "COLD"}
14+
REBOOTMETHOD_WARM_BOOT_VALUES = {4, "WARM"}
15+
16+
# Timeout for SONiC Host Service to be killed during reboot
17+
REBOOT_TIMEOUT = 260
18+
19+
EXECUTE_COLD_REBOOT_COMMAND = "sudo reboot"
20+
EXECUTE_WARM_REBOOT_COMMAND = "/usr/local/bin/warm-reboot -v"
21+
22+
logger = logging.getLogger(__name__)
23+
24+
25+
class GnoiReboot(host_service.HostModule):
26+
"""DBus endpoint that executes the reboot and returns the reboot status
27+
"""
28+
29+
def __init__(self, mod_name):
30+
"""Use threading.lock mechanism to read/write into response_data
31+
since response_data can be read/write by multiple threads"""
32+
self.lock = threading.Lock()
33+
# reboot_status_flag is used to keep track of reboot status on host
34+
self.reboot_status_flag = {}
35+
# Populating with default value i.e., no active reboot
36+
self.populate_reboot_status_flag()
37+
super(GnoiReboot, self).__init__(mod_name)
38+
39+
def populate_reboot_status_flag(self, active = False, when = 0, reason = ""):
40+
"""Populates the reboot_status_flag with given input params"""
41+
self.lock.acquire()
42+
self.reboot_status_flag["active"] = active
43+
self.reboot_status_flag["when"] = when
44+
self.reboot_status_flag["reason"] = reason
45+
self.lock.release()
46+
return
47+
48+
def validate_reboot_request(self, reboot_request):
49+
# Check whether reboot method is present.
50+
if "method" not in reboot_request:
51+
return 1, "Reboot request must contain a reboot method"
52+
53+
# Check whether reboot method is valid.
54+
rebootmethod = reboot_request["method"]
55+
valid_method = False
56+
for values in [REBOOTMETHOD_COLD_BOOT_VALUES, REBOOTMETHOD_WARM_BOOT_VALUES]:
57+
if rebootmethod in values:
58+
valid_method = True
59+
if not valid_method:
60+
return 1, "Invalid reboot method: " + str(rebootmethod)
61+
62+
# Check whether delay is non-zero. delay key will not exist in reboot_request if it is zero
63+
if "delay" in reboot_request and reboot_request["delay"] != 0:
64+
return 1, "Delayed reboot is not supported"
65+
return 0, ""
66+
67+
def execute_reboot(self, rebootmethod):
68+
"""Execute reboot and reset reboot_status_flag when reboot fails"""
69+
70+
if rebootmethod in REBOOTMETHOD_COLD_BOOT_VALUES:
71+
command = EXECUTE_COLD_REBOOT_COMMAND
72+
logger.warning("%s: Issuing cold reboot", MOD_NAME)
73+
elif rebootmethod in REBOOTMETHOD_WARM_BOOT_VALUES:
74+
command = EXECUTE_WARM_REBOOT_COMMAND
75+
logger.warning("%s: Issuing WARM reboot", MOD_NAME)
76+
else:
77+
logger.error("%s: Invalid reboot method: %d", MOD_NAME, rebootmethod)
78+
return
79+
80+
rc, stdout, stderr = _run_command(command)
81+
if rc:
82+
self.populate_reboot_status_flag()
83+
logger.error("%s: Reboot failed execution with stdout: %s, "
84+
"stderr: %s", MOD_NAME, stdout, stderr)
85+
return
86+
87+
"""Wait for 260 seconds for the reboot to complete. Here, we expect that SONiC Host Service
88+
will be killed during this waiting period if the reboot is successful. If this module
89+
is still alive after the below waiting period, we can conclude that the reboot has failed.
90+
Each container can take up to 20 seconds to get killed. In total, there are 10 containers,
91+
and adding a buffer of 1 minute brings up the delay value to be 260 seconds."""
92+
time.sleep(REBOOT_TIMEOUT)
93+
# Conclude that the reboot has failed if we reach this point
94+
self.populate_reboot_status_flag()
95+
return
96+
97+
@host_service.method(host_service.bus_name(MOD_NAME), in_signature='as', out_signature='is')
98+
def issue_reboot(self, options):
99+
"""Issues reboot after performing the following steps sequentially:
100+
1. Checks that reboot_status_flag is not set
101+
2. Validates the reboot request
102+
3. Sets the reboot_status_flag
103+
4. Issues the reboot in a separate thread
104+
"""
105+
logger.warning("%s: issue_reboot rpc called", MOD_NAME)
106+
self.lock.acquire()
107+
is_reboot_ongoing = self.reboot_status_flag["active"]
108+
self.lock.release()
109+
# Return without issuing the reboot if the previous reboot is ongoing
110+
if is_reboot_ongoing:
111+
return 1, "Previous reboot is ongoing"
112+
113+
"""Convert input json formatted reboot request into python dict.
114+
reboot_request is a python dict with the following keys:
115+
method - specifies the method of reboot
116+
delay - delay to issue reboot, key exists only if it is non-zero
117+
message - reason for reboot
118+
force - either true/false, key exists only if it is true
119+
"""
120+
try:
121+
reboot_request = json.loads(options[0])
122+
except ValueError:
123+
return 1, "Failed to parse json formatted reboot request into python dict"
124+
125+
# Validate reboot request
126+
err, errstr = self.validate_reboot_request(reboot_request)
127+
if err:
128+
return err, errstr
129+
130+
# Sets reboot_status_flag to be in active state
131+
self.populate_reboot_status_flag(True, int(time.time()), reboot_request["message"])
132+
133+
# Issue reboot in a new thread and reset the reboot_status_flag if the reboot fails
134+
try:
135+
t = threading.Thread(target=self.execute_reboot, args=(reboot_request["method"],))
136+
t.start()
137+
except RuntimeError as error:
138+
return 1, "Failed to start thread to execute reboot with error: " + str(error)
139+
return 0, "Successfully issued reboot"
140+
141+
@host_service.method(host_service.bus_name(MOD_NAME), in_signature='', out_signature='is')
142+
def get_reboot_status(self):
143+
"""Returns current reboot status on host in json format"""
144+
self.lock.acquire()
145+
response_data = json.dumps(self.reboot_status_flag)
146+
self.lock.release()
147+
return 0, response_data
148+
149+
def register():
150+
"""Return the class name"""
151+
return GnoiReboot, MOD_NAME

scripts/sonic-host-server

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import dbus.service
1212
import dbus.mainloop.glib
1313

1414
from gi.repository import GObject
15-
from host_modules import config_engine, gcu, host_service, showtech, systemd_service, file_service
15+
from host_modules import config_engine, gcu, host_service, showtech, systemd_service, file_service, gnoi_reboot
1616

1717

1818
def register_dbus():
@@ -21,6 +21,7 @@ def register_dbus():
2121
'config': config_engine.Config('config'),
2222
'gcu': gcu.GCU('gcu'),
2323
'host_service': host_service.HostService('host_service'),
24+
'gnoi_reboot': gnoi_reboot.GnoiReboot('gnoi_reboot'),
2425
'showtech': showtech.Showtech('showtech'),
2526
'systemd': systemd_service.SystemdService('systemd'),
2627
'file_stat': file_service.FileService('file')

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
maintainer = 'Joe LeVeque',
3131
maintainer_email = '[email protected]',
3232
packages = [
33-
'host_modules'
33+
'host_modules',
34+
'utils',
3435
],
3536
scripts = [
3637
'scripts/caclmgrd',

0 commit comments

Comments
 (0)