Added target and terminal support.
[src/xds/xds-agent.git] / lib / agent / apiv1-targets.go
diff --git a/lib/agent/apiv1-targets.go b/lib/agent/apiv1-targets.go
new file mode 100644 (file)
index 0000000..5a7862a
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2018 "IoT.bzh"
+ * Author Sebastien Douheret <sebastien@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package agent
+
+import (
+       "fmt"
+       "net/http"
+
+       "gerrit.automotivelinux.org/gerrit/src/xds/xds-agent/lib/xaapiv1"
+       common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib"
+       "gerrit.automotivelinux.org/gerrit/src/xds/xds-server.git/lib/xsapiv1"
+       "github.com/franciscocpg/reflectme"
+       "github.com/gin-gonic/gin"
+       uuid "github.com/satori/go.uuid"
+)
+
+// targetsPassthroughInit Declare passthrough routes for targets
+func (s *APIService) targetsPassthroughInit(svr *XdsServer) error {
+       svr.PassthroughGet("/targets")
+       svr.PassthroughGet("/targets/:id")
+       svr.PassthroughPost("/targets")
+       svr.PassthroughDelete("/targets/:id")
+
+       svr.PassthroughGet("/targets/:id/terminals")
+       svr.PassthroughGet("/targets/:id/terminals/:tid")
+       svr.PassthroughPost("/targets/:id/terminals")
+       svr.PassthroughPut("/targets/:id/terminals/:tid")
+       svr.PassthroughDelete("/targets/:id/terminals/:tid")
+
+       svr.apiRouter.POST("/targets/:id/terminals/:tid/open", s.TargetTerminalOpen)
+
+       svr.PassthroughPost("/targets/:id/terminals/:tid/close")
+       svr.PassthroughPost("/targets/:id/terminals/:tid/resize")
+       svr.PassthroughPost("/targets/:id/terminals/:tid/signal")
+       svr.PassthroughPost("/targets/:id/terminals/:tid/signal/:sig")
+
+       return nil
+}
+
+// GetServerFromTargetID Retrieve XDS Server definition from a target ID
+func (s *APIService) GetServerFromTargetID(targetID, termID string) (*XdsServer, string, error) {
+
+       // FIXME add cache (but take care to support partial term ID)
+       for _, svr := range s.xdsServers {
+               term := xsapiv1.TerminalConfig{}
+               if err := svr.CommandTgtTerminalGet(targetID, termID, &term); err == nil {
+                       return svr, term.ID, nil
+               }
+       }
+       return nil, "", fmt.Errorf("Cannot identify XDS Server")
+}
+
+// TargetTerminalOpen Open a target terminal/console
+func (s *APIService) TargetTerminalOpen(c *gin.Context) {
+
+       // First retrieve Server ID and send command to right server
+       targetID := c.Param("id")
+       svr, termID, err := s.GetServerFromTargetID(targetID, c.Param("tid"))
+       if err != nil {
+               common.APIError(c, err.Error())
+               return
+       }
+
+       // Retrieve session info
+       sess := s.sessions.Get(c)
+       if sess == nil {
+               common.APIError(c, "Unknown sessions")
+               return
+       }
+       sock := sess.IOSocket
+       if sock == nil {
+               common.APIError(c, "Websocket not established")
+               return
+       }
+
+       // Forward input events from client to XDSServer through WS
+       err = (*sock).On(xsapiv1.TerminalInEvent, func(stdin string) {
+               s.LogSillyf("TARGET TERMINAL EVENT IN (%s) <<%v>>", xsapiv1.TerminalInEvent, stdin)
+               svr.EventEmit(xaapiv1.TerminalInEvent, stdin)
+       })
+       if err != nil {
+               msgErr := "Error while registering WS for " + xsapiv1.TerminalInEvent + " event"
+               s.Log.Errorf(msgErr, ", err: %v", err)
+               common.APIError(c, msgErr)
+               return
+       }
+
+       // Forward output events from XDSServer to client through WS
+       var outFwdFuncID uuid.UUID
+       outFwdFunc := func(pData interface{}, evData interface{}) error {
+               sid := pData.(string)
+               // IO socket can be nil when disconnected
+               so := s.sessions.IOSocketGet(sid)
+               if so == nil {
+                       s.Log.Infof("%s not emitted: WS closed (sid:%s)", xaapiv1.TerminalOutEvent, sid)
+                       return nil
+               }
+
+               // Add sessionID to event Data
+               reflectme.SetField(evData, "sessionID", sid)
+
+               s.LogSillyf("TARGET TERMINAL EVENT OUT (%s) <<%v>>", xaapiv1.TerminalOutEvent, evData)
+
+               // Forward event to Client/Dashboard
+               (*so).Emit(xaapiv1.TerminalOutEvent, evData)
+               return nil
+       }
+       outFwdFuncID, err = svr.EventOn(xsapiv1.TerminalOutEvent, sess.ID, outFwdFunc)
+       if err != nil {
+               common.APIError(c, err.Error())
+               return
+       }
+
+       // Handle Exit event separately to cleanup registered listener
+       var exitFuncID uuid.UUID
+       exitFunc := func(privD interface{}, evData interface{}) error {
+               evN := xaapiv1.TerminalExitEvent
+
+               pData := privD.(map[string]string)
+               sid := pData["sessID"]
+
+               // Add sessionID to event Data
+               reflectme.SetField(evData, "sessionID", sid)
+
+               // IO socket can be nil when disconnected
+               so := s.sessions.IOSocketGet(sid)
+               if so != nil {
+                       (*so).Emit(evN, evData)
+               } else {
+                       s.Log.Infof("%s not emitted: WS closed (sid:%s)", evN, sid)
+               }
+
+               // cleanup listener
+               svr.EventOff(xaapiv1.TerminalOutEvent, outFwdFuncID)
+               svr.EventOff(evN, exitFuncID)
+
+               return nil
+       }
+
+       privData := map[string]string{
+               "sessID": sess.ID,
+       }
+       exitFuncID, err = svr.EventOn(xaapiv1.TerminalExitEvent, privData, exitFunc)
+       if err != nil {
+               common.APIError(c, err.Error())
+               return
+       }
+
+       // Forward back command to right server
+       res := xsapiv1.TerminalConfig{}
+       if err := svr.CommandTgtTerminalOpen(targetID, termID, &res); err != nil {
+               common.APIError(c, err.Error())
+               return
+       }
+
+       c.JSON(http.StatusOK, res)
+}