Added Supervision/Monitoring support
[src/xds/xds-agent.git] / lib / agent / xdssupervior.go
diff --git a/lib/agent/xdssupervior.go b/lib/agent/xdssupervior.go
new file mode 100644 (file)
index 0000000..bbe2500
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2017-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"
+       "io"
+       "strings"
+       "time"
+
+       common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib"
+       uuid "github.com/satori/go.uuid"
+)
+
+// XdsSupervisor .
+type XdsSupervisor struct {
+       *Context
+       ID        string
+       BaseURL   string
+       ConnRetry int
+       Connected bool
+       Disabled  bool
+
+       // Private fields
+       client      *common.HTTPClient
+       logOut      io.Writer
+       cbOnConnect OnConnectedXdsSupervCB
+}
+
+// XdsSuperVRequest Resquest field of a reply
+type XdsSuperVRequest struct {
+       Status string `json:"status"`
+       Info   string `json:"info"`
+}
+
+// XdsSuperVReply Reply structure of XDS Supervision Daemon
+type XdsSuperVReply struct {
+       JType    string           `json:"jtype"`
+       Request  XdsSuperVRequest `json:"request"`
+       Response interface{}      `json:"response"`
+}
+
+// XdsSuperVTraceConfig
+type XdsSuperVTraceConfig struct {
+       Pid    int    `json:"pid"`
+       Pids   []int  `json:"pids"`
+       WsName string `json:"ws"`
+}
+
+// OnConnectedXdsSupervCB connect callback
+type OnConnectedXdsSupervCB func(svr *XdsSupervisor) error
+
+// NewXdsSupervisor creates an instance of XdsSupervisor
+func NewXdsSupervisor(ctx *Context) *XdsSupervisor {
+       return &XdsSupervisor{
+               Context:   ctx,
+               ID:        "XdsSupervisor-" + uuid.NewV1().String(),
+               BaseURL:   ctx.Config.FileConf.ProfileConf.XDSBinder.URL,
+               ConnRetry: ctx.Config.FileConf.ProfileConf.XDSBinder.ConnRetry,
+               Connected: false,
+               Disabled:  false,
+
+               logOut: ctx.Log.Out,
+       }
+}
+
+// Connect Establish HTTP connection with XDS Supervisor Dameon
+func (xs *XdsSupervisor) Connect() error {
+       var err error
+       var retry int
+
+       xs.Disabled = false
+       xs.Connected = false
+
+       err = nil
+       for retry = xs.ConnRetry; retry > 0; retry-- {
+               if err = xs._CreateConnectHTTP(); err == nil {
+                       break
+               }
+               if retry == xs.ConnRetry {
+                       // Notify only on the first conn error
+                       // doing that avoid 2 notifs (conn false; conn true) on startup
+                       xs._NotifyState()
+               }
+               xs.Log.Infof("Establishing connection to XDS Supervisor daemon (retry %d/%d)", retry, xs.ConnRetry)
+               time.Sleep(time.Second)
+       }
+       if retry == 0 {
+               // FIXME: re-use _Reconnect to wait longer in background
+               return fmt.Errorf("Connection to XDS Supervisor daemon failure")
+       }
+       if err != nil {
+               return err
+       }
+
+       // Check HTTP connection and establish WS connection
+       err = xs._Connect(false)
+
+       return err
+}
+
+// ConnectOn Register a callback on events reception
+func (xs *XdsSupervisor) ConnectOn(f OnConnectedXdsSupervCB) error {
+       xs.cbOnConnect = f
+       return nil
+}
+
+// GetVersion Send Get request to retrieve XDS Supervision version
+func (xs *XdsSupervisor) GetVersion(res interface{}) error {
+       // FIXME add suffix URLSuffix in common HTTP client lib instead of _BuildURL
+       return xs.client.Get(xs._BuildURL("/version"), &res)
+}
+
+// GetTopo Send Get request to retrieve Services/Daemons topology
+func (xs *XdsSupervisor) GetTopo(res interface{}) error {
+       return xs.client.Get(xs._BuildURL("/list"), &res)
+}
+
+// StartTrace Send Supervisor config and start tracing
+func (xs *XdsSupervisor) StartTrace(cfg XdsSuperVTraceConfig, res interface{}) error {
+       return xs.client.Post(xs._BuildURL("/trace/start"), cfg, &res)
+}
+
+// StopTrace Send Supervisor stop tracing
+func (xs *XdsSupervisor) StopTrace(res interface{}) error {
+       var cfg interface{}
+       return xs.client.Post(xs._BuildURL("/trace/stop"), cfg, res)
+}
+
+/***
+** Private functions
+***/
+
+// _BuildURL .
+func (xs *XdsSupervisor) _BuildURL(url string) string {
+       return url + "?token=HELLO&uuid=magic"
+}
+
+// Create HTTP client
+func (xs *XdsSupervisor) _CreateConnectHTTP() error {
+       var err error
+       // FIXME SEB - Client key not in header but in cookie
+       // temporary workaround: used _BuildURL to append uuid=magic in URL
+       // map[Set-Cookie:[x-afb-uuid-5678=2b185cc3-276b-4097-91fa-d607eaf937e6; Path=/api; Max-Age=32000000; ...
+       //port := strings.Split(xs.BaseURL, ":")[2]
+       //"x-afb-uuid-" + port
+
+       xs.client, err = common.HTTPNewClient(xs.BaseURL,
+               common.HTTPClientConfig{
+                       //HeaderClientKeyName: "Xds-Sid",
+                       HeaderAPIKeyName: "token",
+                       Apikey:           "HELLO",
+                       URLPrefix:        "/api/xds",
+                       CsrfDisable:      true,
+                       LogOut:           xs.logOut,
+                       LogPrefix:        "XDSSUPERV: ",
+                       LogLevel:         common.HTTPLogLevelWarning,
+               })
+
+       xs.client.SetLogLevel(xs.Log.Level.String())
+
+       if err != nil {
+               msg := ": " + err.Error()
+               if strings.Contains(err.Error(), "connection refused") {
+                       msg = fmt.Sprintf("(url: %s)", xs.BaseURL)
+               }
+               return fmt.Errorf("ERROR: cannot connect to XDS Supervisor %s", msg)
+       }
+       if xs.client == nil {
+               return fmt.Errorf("ERROR: cannot connect to XDS Supervisor (null client)")
+       }
+
+       return nil
+}
+
+// _Connect Established HTTP and WS connection
+func (xs *XdsSupervisor) _Connect(reConn bool) error {
+
+       var res interface{}
+       if err := xs.client.Get(xs._BuildURL("/ping"), &res); err != nil {
+               xs.Connected = false
+               if !reConn {
+                       xs._NotifyState()
+               }
+               return err
+       }
+
+       xs.Connected = true
+
+       // Call OnConnect callback
+       if xs.cbOnConnect != nil {
+               xs.cbOnConnect(xs)
+       }
+
+       xs._NotifyState()
+       return nil
+}
+
+// _NotifyState Send event to notify changes
+func (xs *XdsSupervisor) _NotifyState() {
+
+       /* TODO
+       evSts := xaapiv1.ServerCfg{
+               ID:         xs.ID,
+               URL:        xs.BaseURL,
+               APIURL:     xs.APIURL,
+               PartialURL: xs.PartialURL,
+               ConnRetry:  xs.ConnRetry,
+               Connected:  xs.Connected,
+       }
+       if err := xs.events.Emit(xaapiv1.EVTServerConfig, evSts, ""); err != nil {
+               xs.Log.Warningf("Cannot notify XdsServer state change: %v", err)
+       }
+       */
+}