Skip to content

Commit 14e6dcf

Browse files
kanchanavelusamyVSuryaprasad-HCL
authored andcommitted
[gNOI] Added initial implementation of gNOI.OS Install RPC.
1 parent db9afb6 commit 14e6dcf

File tree

9 files changed

+1359
-6
lines changed

9 files changed

+1359
-6
lines changed

common_utils/context.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ const (
4343
GNMI_SET
4444
GNMI_SET_FAIL
4545
GNOI_REBOOT
46+
GNOI_OS_ACTIVATE
47+
GNOI_OS_INSTALL
48+
GNOI_OS_VERIFY
4649
DBUS
4750
DBUS_FAIL
4851
DBUS_APPLY_PATCH_DB
@@ -77,6 +80,12 @@ func (c CounterType) String() string {
7780
return "GNMI set fail"
7881
case GNOI_REBOOT:
7982
return "GNOI reboot"
83+
case GNOI_OS_ACTIVATE:
84+
return "GNOI OS Activate"
85+
case GNOI_OS_INSTALL:
86+
return "GNOI OS Install"
87+
case GNOI_OS_VERIFY:
88+
return "GNOI OS VERIFY"
8089
case DBUS:
8190
return "DBUS"
8291
case DBUS_FAIL:

gnmi_server/gnoi_os.go

Lines changed: 375 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,375 @@
1+
package gnmi
2+
3+
import (
4+
"context"
5+
"fmt"
6+
log "github.com/golang/glog"
7+
ospb "github.com/openconfig/gnoi/os"
8+
ssc "github.com/sonic-net/sonic-gnmi/sonic_service_client"
9+
"google.golang.org/grpc/codes"
10+
"google.golang.org/grpc/status"
11+
json "google.golang.org/protobuf/encoding/protojson"
12+
"io"
13+
"os"
14+
"sync"
15+
)
16+
17+
var (
18+
sem sync.Mutex
19+
)
20+
21+
// ProcessInstallFromBackEnd makes call via the sonic-host-service.
22+
func ProcessInstallFromBackEnd(req string) (string, error) {
23+
sc, err := ssc.NewDbusClient()
24+
if err != nil {
25+
return "", err
26+
}
27+
return sc.OSInstall(req)
28+
}
29+
func handleErrorResponse(f string, a ...any) *ospb.InstallResponse {
30+
errStr := fmt.Sprintf(f, a...)
31+
log.V(1).Infoln(errStr)
32+
return &ospb.InstallResponse{
33+
Response: &ospb.InstallResponse_InstallError{
34+
InstallError: &ospb.InstallError{
35+
Detail: errStr,
36+
},
37+
},
38+
}
39+
}
40+
func (srv *OSServer) processTransferReq(req *ospb.InstallRequest) *ospb.InstallResponse {
41+
trfReq := req.GetTransferRequest()
42+
if trfReq.GetVersion() == "" {
43+
log.V(1).Infoln("TransferRequest must contain a valid OS version.")
44+
return &ospb.InstallResponse{
45+
Response: &ospb.InstallResponse_InstallError{
46+
InstallError: &ospb.InstallError{
47+
Type: ospb.InstallError_PARSE_FAIL,
48+
Detail: "TransferRequest must contain a valid OS version.",
49+
},
50+
},
51+
}
52+
}
53+
// Front end marshals the request, and sends to the sonic-host-service.
54+
// Back end is expected to return the response in JSON format.
55+
reqStr, err := json.Marshal(req)
56+
if err != nil {
57+
return handleErrorResponse("Failed to marshal TransferReady JSON: err: %v, req: %v, reqStr: %v", err, req, reqStr)
58+
}
59+
respStr, err := srv.config.OSCfg.ProcessTrfReady(string(reqStr))
60+
if err != nil {
61+
return handleErrorResponse("Received error from OSServer.TransferReady: err: %v, reqStr: %v, respStr: %v", err, reqStr, respStr)
62+
}
63+
resp := &ospb.InstallResponse{}
64+
if err := json.Unmarshal([]byte(respStr), resp); err != nil {
65+
return handleErrorResponse("Failed to unmarshal TransferReady JSON: err: %v, respStr: %v", err, respStr)
66+
}
67+
return resp
68+
}
69+
func (srv *OSServer) processTransferEnd(req *ospb.InstallRequest) *ospb.InstallResponse {
70+
// Front end marshals the request, and sends to the sonic-host-service.
71+
// Back end is expected to return the response in JSON format.
72+
reqStr, err := json.Marshal(req)
73+
if err != nil {
74+
return handleErrorResponse("Failed to marshal TransferEnd JSON: err: %v, req: %v, reqStr: %v", err, req, reqStr)
75+
}
76+
respStr, err := srv.config.OSCfg.ProcessTrfEnd(string(reqStr))
77+
if err != nil {
78+
return handleErrorResponse("Received error from OSServer.TransferEnd: err: %v, reqStr: %v, respStr: %v", err, reqStr, respStr)
79+
}
80+
resp := &ospb.InstallResponse{}
81+
if err := json.Unmarshal([]byte(respStr), resp); err != nil {
82+
return handleErrorResponse("Failed to unmarshal TransferEnd JSON: err: %v, respStr: %v", err, respStr)
83+
}
84+
return resp
85+
}
86+
func (srv *OSServer) processTransferContent(trfCnt []byte, imgPath string) *ospb.InstallResponse {
87+
errResp := &ospb.InstallResponse{
88+
Response: &ospb.InstallResponse_InstallError{
89+
InstallError: &ospb.InstallError{
90+
Detail: fmt.Sprintf("Failed to open file [%s].", imgPath),
91+
},
92+
},
93+
}
94+
// If the file doesn't exist, create it, or append to the file
95+
f, err := os.OpenFile(imgPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
96+
if err != nil {
97+
log.V(1).Infoln(err)
98+
return errResp
99+
}
100+
if _, err := f.Write(trfCnt); err != nil {
101+
f.Close()
102+
log.V(1).Infoln(err)
103+
return errResp
104+
}
105+
if err := f.Close(); err != nil {
106+
log.V(1).Infoln(err)
107+
return errResp
108+
}
109+
return &ospb.InstallResponse{
110+
Response: &ospb.InstallResponse_TransferProgress{
111+
TransferProgress: &ospb.TransferProgress{
112+
BytesReceived: uint64(len(trfCnt)),
113+
},
114+
},
115+
}
116+
}
117+
func (srv *OSServer) getVersionPath(version string) string {
118+
return srv.config.OSCfg.ImgDir + "/" + version
119+
}
120+
func (srv *OSServer) imageExists(path string) bool {
121+
if _, err := os.Lstat(path); err == nil {
122+
return true
123+
}
124+
return false
125+
}
126+
func (srv *OSServer) removeIncompleteTrf(imgPath string) {
127+
if !srv.imageExists(imgPath) {
128+
return
129+
}
130+
log.V(1).Infoln("Remove incomplete image: ", imgPath)
131+
// Now remove the file.
132+
if err := os.Remove(imgPath); err != nil {
133+
log.V(1).Infoln("Failed to remove incomplete image: ", err)
134+
}
135+
}
136+
137+
// TODO(b/328077908) Alarms to be implemented later
138+
//
139+
// func raiseAlarm(err error) {
140+
// csh, cshErr := common_utils.NewComponentStateHelper(common_utils.Telemetry)
141+
// if cshErr != nil {
142+
// log.V(lvl.ERROR).Infof("gNOI OS: failed to create new ComponentStateHelper - %v", cshErr)
143+
// return
144+
// }
145+
// defer csh.Close()
146+
// if rcsErr := csh.ReportComponentState(common_utils.ComponentMinor, err.Error()); rcsErr != nil {
147+
// log.V(lvl.ERROR).Infof("Failed to raise ComponentMinor Alarm: %v", rcsErr)
148+
// }
149+
// }
150+
//
151+
// Install implements correspondig RPC
152+
func (srv *OSServer) Install(stream ospb.OS_InstallServer) error {
153+
ctx := stream.Context()
154+
ctx, err := authenticate(srv.config, ctx, "gnoi", false)
155+
if err != nil {
156+
return err
157+
}
158+
log.V(1).Info("gNOI: os.Install")
159+
// Concurrent Install RPCs are not allowed.
160+
if !sem.TryLock() {
161+
log.V(1).Infoln("Concurrent Install RPCs are not allowed.")
162+
// Send InstallError response.
163+
err = stream.Send(&ospb.InstallResponse{
164+
Response: &ospb.InstallResponse_InstallError{
165+
InstallError: &ospb.InstallError{
166+
Type: ospb.InstallError_INSTALL_IN_PROGRESS,
167+
Detail: "Concurrent Install RPCs are not allowed.",
168+
},
169+
},
170+
})
171+
if err != nil {
172+
log.V(1).Infoln("Error while sending InstallError response: ", err)
173+
return status.Errorf(codes.Aborted, err.Error())
174+
}
175+
return status.Errorf(codes.Aborted, "Concurrent Install RPCs are not allowed.")
176+
}
177+
defer sem.Unlock()
178+
// Receive TransferReq message.
179+
req, err := stream.Recv()
180+
if err == io.EOF {
181+
log.V(1).Infoln("Received EOF instead of TransferRequest!")
182+
return nil
183+
}
184+
if err != nil {
185+
log.V(1).Infoln("Received error: ", err)
186+
// TODO(b/328077908) Alarms to be implemented later
187+
// raiseAlarm(err)
188+
return status.Errorf(codes.Aborted, err.Error())
189+
}
190+
trfReq := req.GetTransferRequest()
191+
if trfReq == nil {
192+
log.V(1).Infoln("Did not receive a TransferRequest.")
193+
err = status.Errorf(codes.InvalidArgument, "Expected TransferRequest.")
194+
// TODO(b/328077908) Alarms to be implemented later
195+
// raiseAlarm(err)
196+
return err
197+
}
198+
resp := srv.processTransferReq(req)
199+
if resp != nil {
200+
if err := stream.Send(resp); err != nil {
201+
log.V(1).Infoln("Error while sending response: ", err)
202+
// TODO(b/328077908) Alarms to be implemented later
203+
// raiseAlarm(err)
204+
return status.Errorf(codes.Aborted, err.Error())
205+
}
206+
}
207+
if resp == nil || resp.GetInstallError() != nil {
208+
err = status.Errorf(codes.Aborted, "Failed to process TransferRequest.")
209+
// TODO(b/328077908) Alarms to be implemented later
210+
// raiseAlarm(err)
211+
return err
212+
}
213+
imgPath := srv.getVersionPath(trfReq.GetVersion())
214+
imgTrfInitiated := false
215+
for {
216+
req, err = stream.Recv()
217+
if err == io.EOF {
218+
log.V(1).Infoln("Received EOF instead of TransferContent request!")
219+
if imgTrfInitiated {
220+
srv.removeIncompleteTrf(imgPath)
221+
}
222+
return nil
223+
}
224+
if err != nil {
225+
log.V(1).Infoln("Received error: ", err)
226+
if imgTrfInitiated {
227+
srv.removeIncompleteTrf(imgPath)
228+
}
229+
// TODO(b/328077908) Alarms to be implemented later
230+
// raiseAlarm(err)
231+
return status.Errorf(codes.Aborted, err.Error())
232+
}
233+
if trfReq := req.GetTransferRequest(); trfReq != nil {
234+
log.V(1).Infoln("Received a TransferReq out-of-sequence.")
235+
if imgTrfInitiated {
236+
srv.removeIncompleteTrf(imgPath)
237+
}
238+
err = status.Errorf(codes.InvalidArgument, "Expected TransferContent, or TransferEnd.")
239+
// TODO(b/328077908) Alarms to be implemented later
240+
// raiseAlarm(err)
241+
return err
242+
}
243+
// Transferring content is complete.
244+
if trfEnd := req.GetTransferEnd(); trfEnd != nil {
245+
break
246+
}
247+
// Process content transfer.
248+
// If image exists, target should have sent Validated | InstallError on TransferRequest.
249+
if !imgTrfInitiated && srv.imageExists(imgPath) {
250+
resp := &ospb.InstallResponse{
251+
Response: &ospb.InstallResponse_InstallError{
252+
InstallError: &ospb.InstallError{
253+
Detail: fmt.Sprintf("File exists [%s]!", imgPath),
254+
},
255+
},
256+
}
257+
if err := stream.Send(resp); err != nil {
258+
log.V(1).Infoln("Error while sending response: ", err)
259+
}
260+
err = status.Errorf(codes.Aborted, "Failed as image exists!")
261+
// TODO(b/328077908) Alarms to be implemented later
262+
// raiseAlarm(err)
263+
return err
264+
}
265+
imgTrfInitiated = true
266+
resp := srv.processTransferContent(req.GetTransferContent(), imgPath)
267+
if resp != nil {
268+
if err := stream.Send(resp); err != nil {
269+
log.V(1).Infoln("Error while sending response: ", err)
270+
srv.removeIncompleteTrf(imgPath)
271+
// TODO(b/328077908) Alarms to be implemented later
272+
// raiseAlarm(err)
273+
return status.Errorf(codes.Aborted, err.Error())
274+
}
275+
}
276+
if resp == nil || resp.GetInstallError() != nil {
277+
srv.removeIncompleteTrf(imgPath)
278+
err = status.Errorf(codes.Aborted, "Failed to process TransferContent.")
279+
// TODO(b/328077908) Alarms to be implemented later
280+
// raiseAlarm(err)
281+
return err
282+
}
283+
}
284+
// Receive TransferEnd message.
285+
trfEnd := req.GetTransferEnd()
286+
if trfEnd == nil {
287+
log.V(1).Infoln("Did not receive a TransferEnd")
288+
srv.removeIncompleteTrf(imgPath)
289+
err = status.Errorf(codes.InvalidArgument, "Expected TransferEnd")
290+
// TODO(b/328077908) Alarms to be implemented later
291+
// raiseAlarm(err)
292+
return err
293+
}
294+
resp = srv.processTransferEnd(req)
295+
if resp != nil {
296+
if err := stream.Send(resp); err != nil {
297+
log.V(1).Infoln("Error while sending response: ", err)
298+
srv.removeIncompleteTrf(imgPath)
299+
// TODO(b/328077908) Alarms to be implemented later
300+
// raiseAlarm(err)
301+
return status.Errorf(codes.Aborted, err.Error())
302+
}
303+
}
304+
if resp == nil || resp.GetInstallError() != nil {
305+
srv.removeIncompleteTrf(imgPath)
306+
err = status.Errorf(codes.Aborted, "Failed to process TransferEnd.")
307+
// TODO(b/328077908) Alarms to be implemented later
308+
// raiseAlarm(err)
309+
return err
310+
}
311+
log.V(1).Info("OS.Install is complete.")
312+
return nil
313+
}
314+
315+
// Activate implements correspondig RPC
316+
func (srv *OSServer) OSActivate(ctx context.Context, req *ospb.ActivateRequest) (*ospb.ActivateResponse, error) {
317+
ctx, err := authenticate(srv.config, ctx, "gnoi", false)
318+
if err != nil {
319+
return nil, err
320+
}
321+
log.V(1).Info("gNOI: os.Activate")
322+
// Front end marshals the request, and sends to the sonic-host-service.
323+
// Back end is expected to return the response in JSON format.
324+
reqStr, err := json.Marshal(req)
325+
if err != nil {
326+
// TODO(b/328077908) Alarms to be implemented later
327+
// raiseAlarm(err)
328+
return nil, status.Errorf(codes.Internal, fmt.Sprintf("Cannot marshal the Activate request: [%s].", req.String()))
329+
}
330+
sc, err := ssc.NewDbusClient()
331+
if err != nil {
332+
return nil, err
333+
}
334+
respStr, err := sc.OSActivate(string(reqStr))
335+
if err != nil {
336+
// TODO(b/328077908) Alarms to be implemented later
337+
// raiseAlarm(err)
338+
return nil, status.Errorf(codes.Internal, err.Error())
339+
}
340+
resp := &ospb.ActivateResponse{}
341+
if err := json.Unmarshal([]byte(respStr), resp); err != nil {
342+
// TODO(b/328077908) Alarms to be implemented later
343+
// raiseAlarm(err)
344+
return nil, status.Errorf(codes.Internal, fmt.Sprintf("Cannot unmarshal the Activate response: [%s].", respStr))
345+
}
346+
return resp, nil
347+
}
348+
349+
// Verify implements correspondig RPC
350+
func (srv *OSServer) OSVerify(ctx context.Context, req *ospb.VerifyRequest) (*ospb.VerifyResponse, error) {
351+
ctx, err := authenticate(srv.config, ctx, "gnoi", false)
352+
if err != nil {
353+
return nil, err
354+
}
355+
log.V(1).Info("gNOI: os.Verify")
356+
// Front end marshals the request, and sends to the sonic-host-service.
357+
// Back end is expected to return the response in JSON format.
358+
reqStr, err := json.Marshal(req)
359+
if err != nil {
360+
return nil, status.Errorf(codes.Internal, fmt.Sprintf("Cannot marshal the Verify request: [%s].", req.String()))
361+
}
362+
sc, err := ssc.NewDbusClient()
363+
if err != nil {
364+
return nil, err
365+
}
366+
respStr, err := sc.OSVerify(string(reqStr))
367+
if err != nil {
368+
return nil, status.Errorf(codes.Internal, err.Error())
369+
}
370+
resp := &ospb.VerifyResponse{}
371+
if err := json.Unmarshal([]byte(respStr), resp); err != nil {
372+
return nil, status.Errorf(codes.Internal, fmt.Sprintf("Cannot unmarshal the Verify response: [%s].", respStr))
373+
}
374+
return resp, nil
375+
}

0 commit comments

Comments
 (0)