2 * Copyright (C) 2018 "IoT.bzh"
3 * Author Sebastien Douheret <sebastien@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
25 "gerrit.automotivelinux.org/gerrit/src/xds/xds-agent.git/lib/xaapiv1"
26 common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git"
27 "gerrit.automotivelinux.org/gerrit/src/xds/xds-server.git/lib/xsapiv1"
28 "github.com/franciscocpg/reflectme"
29 "github.com/gin-gonic/gin"
30 uuid "github.com/satori/go.uuid"
33 // targetsPassthroughInit Declare passthrough routes for targets
34 func (s *APIService) targetsPassthroughInit(svr *XdsServer) error {
35 svr.PassthroughGet("/targets")
36 svr.PassthroughGet("/targets/:id")
37 svr.PassthroughPost("/targets")
38 svr.PassthroughDelete("/targets/:id")
40 svr.PassthroughGet("/targets/:id/terminals")
41 svr.PassthroughGet("/targets/:id/terminals/:tid")
42 svr.PassthroughPost("/targets/:id/terminals")
43 svr.PassthroughPut("/targets/:id/terminals/:tid")
44 svr.PassthroughDelete("/targets/:id/terminals/:tid")
46 svr.apiRouter.POST("/targets/:id/terminals/:tid/open", s.TargetTerminalOpen)
48 svr.PassthroughPost("/targets/:id/terminals/:tid/close")
49 svr.PassthroughPost("/targets/:id/terminals/:tid/resize")
50 svr.PassthroughPost("/targets/:id/terminals/:tid/signal")
51 svr.PassthroughPost("/targets/:id/terminals/:tid/signal/:sig")
56 // targetsEventsForwardInit Register events forwarder for targets
57 func (s *APIService) targetsEventsForwardInit(svr *XdsServer) error {
60 return fmt.Errorf("Cannot register events: XDS Server %v not connected", svr.ID)
63 // Forward Target events from XDS-server to client
64 if _, err := svr.EventOn(xsapiv1.EVTTargetAdd, xaapiv1.EVTTargetAdd, s._targetsEventCB); err != nil {
65 s.Log.Errorf("XDS Server EventOn '%s' failed: %v", xsapiv1.EVTTargetAdd, err)
68 if _, err := svr.EventOn(xsapiv1.EVTTargetRemove, xaapiv1.EVTTargetRemove, s._targetsEventCB); err != nil {
69 s.Log.Errorf("XDS Server EventOn '%s' failed: %v", xsapiv1.EVTTargetRemove, err)
72 if _, err := svr.EventOn(xsapiv1.EVTTargetStateChange, xaapiv1.EVTTargetStateChange, s._targetsEventCB); err != nil {
73 s.Log.Errorf("XDS Server EventOn '%s' failed: %v", xsapiv1.EVTTargetStateChange, err)
80 func (s *APIService) _targetsEventCB(privD interface{}, data interface{}) error {
81 evt := xsapiv1.EventMsg{}
82 d, err := json.Marshal(data)
84 s.Log.Errorf("Cannot marshal XDS Server Target event err=%v, data=%v", err, data)
87 if err = json.Unmarshal(d, &evt); err != nil {
88 s.Log.Errorf("Cannot unmarshal XDS Server Target event err=%v, d=%v", err, string(d))
92 // assume that xsapiv1.TargetConfig == xaapiv1.TargetConfig
93 target, err := evt.DecodeTargetEvent()
95 s.Log.Errorf("Cannot decode XDS Server Target event: err=%v, data=%v", err, data)
99 evtName := privD.(string)
101 if err := s.events.Emit(evtName, target, ""); err != nil {
102 s.Log.Warningf("Cannot notify %s (from server): %v", evtName, err)
108 // terminalsEventsForwardInit Register events forwarder for terminals
109 func (s *APIService) terminalsEventsForwardInit(svr *XdsServer) error {
112 return fmt.Errorf("Cannot register events: XDS Server %v not connected", svr.ID)
115 // Forward Terminal events from XDS-server to client
116 if _, err := svr.EventOn(xsapiv1.EVTTargetTerminalAdd, xaapiv1.EVTTargetTerminalAdd, s._terminalsEventCB); err != nil {
117 s.Log.Errorf("XDS Server EventOn '%s' failed: %v", xsapiv1.EVTTargetTerminalAdd, err)
120 if _, err := svr.EventOn(xsapiv1.EVTTargetTerminalRemove, xaapiv1.EVTTargetTerminalRemove, s._terminalsEventCB); err != nil {
121 s.Log.Errorf("XDS Server EventOn '%s' failed: %v", xsapiv1.EVTTargetTerminalRemove, err)
124 if _, err := svr.EventOn(xsapiv1.EVTTargetTerminalStateChange, xaapiv1.EVTTargetTerminalStateChange, s._terminalsEventCB); err != nil {
125 s.Log.Errorf("XDS Server EventOn '%s' failed: %v", xsapiv1.EVTTargetTerminalStateChange, err)
132 func (s *APIService) _terminalsEventCB(privD interface{}, data interface{}) error {
133 evt := xsapiv1.EventMsg{}
134 d, err := json.Marshal(data)
136 s.Log.Errorf("Cannot marshal XDS Server Target event err=%v, data=%v", err, data)
139 if err = json.Unmarshal(d, &evt); err != nil {
140 s.Log.Errorf("Cannot unmarshal XDS Server Target event err=%v, d=%v", err, string(d))
144 // assume that xsapiv1.TargetConfig == xaapiv1.TargetConfig
145 target, err := evt.DecodeTerminalEvent()
147 s.Log.Errorf("Cannot decode XDS Server Target event: err=%v, data=%v", err, data)
151 evtName := privD.(string)
153 if err := s.events.Emit(evtName, target, ""); err != nil {
154 s.Log.Warningf("Cannot notify %s (from server): %v", evtName, err)
160 // GetServerFromTargetID Retrieve XDS Server definition from a target ID
161 func (s *APIService) GetServerFromTargetID(targetID, termID string) (*XdsServer, string, error) {
163 // FIXME add cache (but take care to support partial term ID)
164 for _, svr := range s.xdsServers {
165 term := xsapiv1.TerminalConfig{}
166 if err := svr.CommandTgtTerminalGet(targetID, termID, &term); err == nil {
167 return svr, term.ID, nil
170 return nil, "", fmt.Errorf("Cannot identify XDS Server")
173 // TargetTerminalOpen Open a target terminal/console
174 func (s *APIService) TargetTerminalOpen(c *gin.Context) {
176 // First retrieve Server ID and send command to right server
177 targetID := c.Param("id")
178 svr, termID, err := s.GetServerFromTargetID(targetID, c.Param("tid"))
180 common.APIError(c, err.Error())
184 // Retrieve session info
185 sess := s.sessions.Get(c)
187 common.APIError(c, "Unknown sessions")
190 sock := sess.IOSocket
192 common.APIError(c, "Websocket not established")
196 // Forward input events from client to XDSServer through WS
197 err = (*sock).On(xsapiv1.TerminalInEvent, func(stdin string) {
198 s.LogSillyf("TARGET TERMINAL EVENT IN (%s) <<%v>>", xsapiv1.TerminalInEvent, stdin)
199 svr.EventEmit(xaapiv1.TerminalInEvent, stdin)
202 msgErr := "Error while registering WS for " + xsapiv1.TerminalInEvent + " event"
203 s.Log.Errorf(msgErr, ", err: %v", err)
204 common.APIError(c, msgErr)
208 // Forward output events from XDSServer to client through WS
209 var outFwdFuncID uuid.UUID
210 outFwdFunc := func(pData interface{}, evData interface{}) error {
211 sid := pData.(string)
212 // IO socket can be nil when disconnected
213 so := s.sessions.IOSocketGet(sid)
215 s.Log.Infof("%s not emitted: WS closed (sid:%s)", xaapiv1.TerminalOutEvent, sid)
219 // Add sessionID to event Data
220 reflectme.SetField(evData, "sessionID", sid)
222 s.LogSillyf("TARGET TERMINAL EVENT OUT (%s) <<%v>>", xaapiv1.TerminalOutEvent, evData)
224 // Forward event to Client/Dashboard
225 (*so).Emit(xaapiv1.TerminalOutEvent, evData)
228 outFwdFuncID, err = svr.EventOn(xsapiv1.TerminalOutEvent, sess.ID, outFwdFunc)
230 common.APIError(c, err.Error())
234 // Handle Exit event separately to cleanup registered listener
235 var exitFuncID uuid.UUID
236 exitFunc := func(privD interface{}, evData interface{}) error {
237 evN := xaapiv1.TerminalExitEvent
239 pData := privD.(map[string]string)
240 sid := pData["sessID"]
242 // Add sessionID to event Data
243 reflectme.SetField(evData, "sessionID", sid)
245 // IO socket can be nil when disconnected
246 so := s.sessions.IOSocketGet(sid)
248 (*so).Emit(evN, evData)
250 s.Log.Infof("%s not emitted: WS closed (sid:%s)", evN, sid)
254 svr.EventOff(xaapiv1.TerminalOutEvent, outFwdFuncID)
255 svr.EventOff(evN, exitFuncID)
260 privData := map[string]string{
263 exitFuncID, err = svr.EventOn(xaapiv1.TerminalExitEvent, privData, exitFunc)
265 common.APIError(c, err.Error())
269 // Forward back command to right server
270 res := xsapiv1.TerminalConfig{}
271 if err := svr.CommandTgtTerminalOpen(targetID, termID, &res); err != nil {
272 common.APIError(c, err.Error())
276 c.JSON(http.StatusOK, res)