Skip to content

Commit 1d8e216

Browse files
authored
gNOI System APIs (Reboot, RebootStatus, CancelReboot) changes (#308)
Why I did it Add support for gNOI System APIs for Reboot, RebootStatus, CancelReboot to forward requests to the Reboot Backend through Redis DB Notification channel. How I did it Created separate files for the System APIs under gnoi_system.go How to verify it Unit tests and build infrastructure checks Description for the changelog Add support for gNOI System APIs for Reboot, RebootStatus, CancelReboot
1 parent 403fa71 commit 1d8e216

14 files changed

+2260
-261
lines changed

common_utils/context.go

-3
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ const (
5454
DBUS_STOP_SERVICE
5555
DBUS_RESTART_SERVICE
5656
DBUS_FILE_STAT
57-
DBUS_HALT_SYSTEM
5857
DBUS_IMAGE_DOWNLOAD
5958
DBUS_IMAGE_INSTALL
6059
DBUS_IMAGE_LIST
@@ -96,8 +95,6 @@ func (c CounterType) String() string {
9695
return "DBUS restart service"
9796
case DBUS_FILE_STAT:
9897
return "DBUS file stat"
99-
case DBUS_HALT_SYSTEM:
100-
return "DBUS halt system"
10198
case DBUS_IMAGE_DOWNLOAD:
10299
return "DBUS image download"
103100
case DBUS_IMAGE_INSTALL:

common_utils/notification_producer.go

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package common_utils
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/go-redis/redis"
8+
log "github.com/golang/glog"
9+
sdcfg "github.com/sonic-net/sonic-gnmi/sonic_db_config"
10+
)
11+
12+
const (
13+
dbName = "STATE_DB"
14+
)
15+
16+
func GetRedisDBClient() (*redis.Client, error) {
17+
ns, _ := sdcfg.GetDbDefaultNamespace()
18+
addr, err := sdcfg.GetDbTcpAddr(dbName, ns)
19+
if err != nil {
20+
log.Errorf("Addr err: %v", err)
21+
return nil, err
22+
}
23+
db, err := sdcfg.GetDbId("STATE_DB", ns)
24+
if err != nil {
25+
log.Errorf("DB err: %v", err)
26+
return nil, err
27+
}
28+
rclient := redis.NewClient(&redis.Options{
29+
Network: "tcp",
30+
Addr: addr,
31+
Password: "", // no password set
32+
DB: db,
33+
DialTimeout: 0,
34+
})
35+
if rclient == nil {
36+
return nil, fmt.Errorf("Cannot create redis client.")
37+
}
38+
if _, err := rclient.Ping().Result(); err != nil {
39+
return nil, err
40+
}
41+
return rclient, nil
42+
}
43+
44+
// NotificationProducer provides utilities for sending messages using notification channel.
45+
// NewNotificationProducer must be called for a new producer.
46+
// Close must be called when finished.
47+
type NotificationProducer struct {
48+
ch string
49+
rc *redis.Client
50+
}
51+
52+
// NewNotificationProducer returns a new NotificationProducer.
53+
func NewNotificationProducer(ch string) (*NotificationProducer, error) {
54+
n := new(NotificationProducer)
55+
n.ch = ch
56+
57+
// Create redis client.
58+
var err error
59+
n.rc, err = GetRedisDBClient()
60+
if err != nil {
61+
return nil, err
62+
}
63+
64+
return n, nil
65+
}
66+
67+
// Close performs cleanup works.
68+
// Close must be called when finished.
69+
func (n *NotificationProducer) Close() {
70+
if n.rc != nil {
71+
n.rc.Close()
72+
}
73+
}
74+
75+
func (n *NotificationProducer) Send(op, data string, kvs map[string]string) error {
76+
fvs := []string{op, data}
77+
for k, v := range kvs {
78+
fvs = append(fvs, k)
79+
fvs = append(fvs, v)
80+
}
81+
82+
val, err := json.Marshal(fvs)
83+
if err != nil {
84+
log.Error(err.Error())
85+
return err
86+
}
87+
log.Infof("Publishing to channel %s: %v.", n.ch, string(val))
88+
return n.rc.Publish(n.ch, val).Err()
89+
}
+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package common_utils
2+
3+
import (
4+
"testing"
5+
)
6+
7+
const (
8+
channel string = "channel"
9+
)
10+
11+
func TestNotificationProducerSucceedsWithEmptyOp(t *testing.T) {
12+
n, _ := NewNotificationProducer(channel)
13+
defer n.Close()
14+
if err := n.Send("", "somedata", map[string]string{}); err != nil {
15+
t.Fatalf("Expected no error!")
16+
}
17+
}
18+
19+
func TestNotificationProducerSucceedsWithEmptyData(t *testing.T) {
20+
n, _ := NewNotificationProducer(channel)
21+
defer n.Close()
22+
if err := n.Send("someop", "", map[string]string{}); err != nil {
23+
t.Fatalf("Expected no error!")
24+
}
25+
}
26+
27+
func TestNotificationProducerSucceedsWithEmptyOpAndData(t *testing.T) {
28+
n, _ := NewNotificationProducer(channel)
29+
defer n.Close()
30+
if err := n.Send("", "", map[string]string{}); err != nil {
31+
t.Fatalf("Expected no error!")
32+
}
33+
}
34+
35+
func TestNotificationProducerSucceedsWithEmptyKeyValues(t *testing.T) {
36+
n, _ := NewNotificationProducer(channel)
37+
defer n.Close()
38+
if err := n.Send("someop", "somedata", map[string]string{}); err != nil {
39+
t.Fatalf("Expected no error!")
40+
}
41+
}
42+
43+
func TestNotificationProducerSucceeds(t *testing.T) {
44+
n, _ := NewNotificationProducer(channel)
45+
defer n.Close()
46+
if err := n.Send("someop", "somedata", map[string]string{"somekey": "somevalue"}); err != nil {
47+
t.Fatalf("Expected no error!")
48+
}
49+
}

gnmi_server/gnoi.go

+4-180
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package gnmi
33
import (
44
"context"
55
"encoding/json"
6-
"errors"
7-
io "io/ioutil"
86
"os"
97
"os/user"
108
"strconv"
@@ -15,8 +13,6 @@ import (
1513
log "github.com/golang/glog"
1614
gnoi_file_pb "github.com/openconfig/gnoi/file"
1715
gnoi_os_pb "github.com/openconfig/gnoi/os"
18-
gnoi_system_pb "github.com/openconfig/gnoi/system"
19-
"github.com/sonic-net/sonic-gnmi/common_utils"
2016
spb "github.com/sonic-net/sonic-gnmi/proto/gnoi"
2117
spb_jwt "github.com/sonic-net/sonic-gnmi/proto/gnoi/jwt"
2218
ssc "github.com/sonic-net/sonic-gnmi/sonic_service_client"
@@ -25,6 +21,10 @@ import (
2521
"google.golang.org/grpc/status"
2622
)
2723

24+
const (
25+
stateDB string = "STATE_DB"
26+
)
27+
2828
func ReadFileStat(path string) (*gnoi_file_pb.StatInfo, error) {
2929
sc, err := ssc.NewDbusClient()
3030
if err != nil {
@@ -97,29 +97,6 @@ func (srv *FileServer) Get(req *gnoi_file_pb.GetRequest, stream gnoi_file_pb.Fil
9797
return status.Errorf(codes.Unimplemented, "")
9898
}
9999

100-
func KillOrRestartProcess(restart bool, serviceName string) error {
101-
sc, err := ssc.NewDbusClient()
102-
if err != nil {
103-
return err
104-
}
105-
defer sc.Close()
106-
107-
if restart {
108-
log.V(2).Infof("Restarting service %s...", serviceName)
109-
err = sc.RestartService(serviceName)
110-
if err != nil {
111-
log.V(2).Infof("Failed to restart service %s: %v", serviceName, err)
112-
}
113-
} else {
114-
log.V(2).Infof("Stopping service %s...", serviceName)
115-
err = sc.StopService(serviceName)
116-
if err != nil {
117-
log.V(2).Infof("Failed to stop service %s: %v", serviceName, err)
118-
}
119-
}
120-
return err
121-
}
122-
123100
func (srv *OSServer) Verify(ctx context.Context, req *gnoi_os_pb.VerifyRequest) (*gnoi_os_pb.VerifyResponse, error) {
124101
_, err := authenticate(srv.config, ctx, false)
125102
if err != nil {
@@ -212,159 +189,6 @@ func (srv *OSServer) Activate(ctx context.Context, req *gnoi_os_pb.ActivateReque
212189
return &resp, nil
213190
}
214191

215-
func (srv *SystemServer) KillProcess(ctx context.Context, req *gnoi_system_pb.KillProcessRequest) (*gnoi_system_pb.KillProcessResponse, error) {
216-
_, err := authenticate(srv.config, ctx, true)
217-
if err != nil {
218-
return nil, err
219-
}
220-
221-
serviceName := req.GetName()
222-
restart := req.GetRestart()
223-
if req.GetPid() != 0 {
224-
return nil, status.Errorf(codes.Unimplemented, "Pid option is not implemented")
225-
}
226-
if req.GetSignal() != gnoi_system_pb.KillProcessRequest_SIGNAL_TERM {
227-
return nil, status.Errorf(codes.Unimplemented, "KillProcess only supports SIGNAL_TERM (option 1) for graceful process termination. Please specify SIGNAL_TERM")
228-
}
229-
log.V(1).Info("gNOI: KillProcess with optional restart")
230-
log.V(1).Info("Request: ", req)
231-
err = KillOrRestartProcess(restart, serviceName)
232-
if err != nil {
233-
return nil, err
234-
}
235-
var resp gnoi_system_pb.KillProcessResponse
236-
return &resp, nil
237-
}
238-
239-
func HaltSystem() error {
240-
sc, err := ssc.NewDbusClient()
241-
if err != nil {
242-
return err
243-
}
244-
defer sc.Close()
245-
246-
log.V(2).Infof("Halting the system..")
247-
err = sc.HaltSystem()
248-
if err != nil {
249-
log.V(2).Infof("Failed to Halt the system %v", err)
250-
}
251-
return err
252-
}
253-
254-
func RebootSystem(fileName string) error {
255-
log.V(2).Infof("Rebooting with %s...", fileName)
256-
sc, err := ssc.NewDbusClient()
257-
if err != nil {
258-
return err
259-
}
260-
defer sc.Close()
261-
262-
err = sc.ConfigReload(fileName)
263-
return err
264-
}
265-
266-
func (srv *SystemServer) Reboot(ctx context.Context, req *gnoi_system_pb.RebootRequest) (*gnoi_system_pb.RebootResponse, error) {
267-
fileName := common_utils.GNMI_WORK_PATH + "/config_db.json.tmp"
268-
269-
_, err := authenticate(srv.config, ctx, true)
270-
if err != nil {
271-
return nil, err
272-
}
273-
log.V(1).Info("gNOI: Reboot")
274-
log.V(1).Info("Request:", req)
275-
276-
// Check the reboot type
277-
switch req.GetMethod() {
278-
case gnoi_system_pb.RebootMethod_HALT:
279-
log.V(1).Info("Reboot method is HALT. Halting the system...")
280-
err = HaltSystem()
281-
if err != nil {
282-
return nil, err
283-
}
284-
default:
285-
log.V(1).Info("Reboot system now, delay is ignored...")
286-
// TODO: Support GNOI reboot delay
287-
// Delay in nanoseconds before issuing reboot.
288-
// https://github.com/openconfig/gnoi/blob/master/system/system.proto#L102-L115
289-
config_db_json, err := io.ReadFile(fileName)
290-
if errors.Is(err, os.ErrNotExist) {
291-
fileName = ""
292-
}
293-
err = RebootSystem(string(config_db_json))
294-
if err != nil {
295-
return nil, err
296-
}
297-
}
298-
299-
var resp gnoi_system_pb.RebootResponse
300-
return &resp, nil
301-
}
302-
303-
// TODO: Support GNOI RebootStatus
304-
func (srv *SystemServer) RebootStatus(ctx context.Context, req *gnoi_system_pb.RebootStatusRequest) (*gnoi_system_pb.RebootStatusResponse, error) {
305-
_, err := authenticate(srv.config, ctx, false)
306-
if err != nil {
307-
return nil, err
308-
}
309-
log.V(1).Info("gNOI: RebootStatus")
310-
return nil, status.Errorf(codes.Unimplemented, "")
311-
}
312-
313-
// TODO: Support GNOI CancelReboot
314-
func (srv *SystemServer) CancelReboot(ctx context.Context, req *gnoi_system_pb.CancelRebootRequest) (*gnoi_system_pb.CancelRebootResponse, error) {
315-
_, err := authenticate(srv.config, ctx, true)
316-
if err != nil {
317-
return nil, err
318-
}
319-
log.V(1).Info("gNOI: CancelReboot")
320-
return nil, status.Errorf(codes.Unimplemented, "")
321-
}
322-
func (srv *SystemServer) Ping(req *gnoi_system_pb.PingRequest, rs gnoi_system_pb.System_PingServer) error {
323-
ctx := rs.Context()
324-
_, err := authenticate(srv.config, ctx, true)
325-
if err != nil {
326-
return err
327-
}
328-
log.V(1).Info("gNOI: Ping")
329-
return status.Errorf(codes.Unimplemented, "")
330-
}
331-
func (srv *SystemServer) Traceroute(req *gnoi_system_pb.TracerouteRequest, rs gnoi_system_pb.System_TracerouteServer) error {
332-
ctx := rs.Context()
333-
_, err := authenticate(srv.config, ctx, true)
334-
if err != nil {
335-
return err
336-
}
337-
log.V(1).Info("gNOI: Traceroute")
338-
return status.Errorf(codes.Unimplemented, "")
339-
}
340-
func (srv *SystemServer) SetPackage(rs gnoi_system_pb.System_SetPackageServer) error {
341-
ctx := rs.Context()
342-
_, err := authenticate(srv.config, ctx, true)
343-
if err != nil {
344-
return err
345-
}
346-
log.V(1).Info("gNOI: SetPackage")
347-
return status.Errorf(codes.Unimplemented, "")
348-
}
349-
func (srv *SystemServer) SwitchControlProcessor(ctx context.Context, req *gnoi_system_pb.SwitchControlProcessorRequest) (*gnoi_system_pb.SwitchControlProcessorResponse, error) {
350-
_, err := authenticate(srv.config, ctx, true)
351-
if err != nil {
352-
return nil, err
353-
}
354-
log.V(1).Info("gNOI: SwitchControlProcessor")
355-
return nil, status.Errorf(codes.Unimplemented, "")
356-
}
357-
func (srv *SystemServer) Time(ctx context.Context, req *gnoi_system_pb.TimeRequest) (*gnoi_system_pb.TimeResponse, error) {
358-
_, err := authenticate(srv.config, ctx, false)
359-
if err != nil {
360-
return nil, err
361-
}
362-
log.V(1).Info("gNOI: Time")
363-
var tm gnoi_system_pb.TimeResponse
364-
tm.Time = uint64(time.Now().UnixNano())
365-
return &tm, nil
366-
}
367-
368192
func (srv *Server) Authenticate(ctx context.Context, req *spb_jwt.AuthenticateRequest) (*spb_jwt.AuthenticateResponse, error) {
369193
// Can't enforce normal authentication here.. maybe only enforce client cert auth if enabled?
370194
// ctx,err := authenticate(srv.config, ctx, false)

0 commit comments

Comments
 (0)