Improve compatibility checking
[govpp.git] / adapter / socketclient / socketclient.go
index 96f23e6..366163f 100644 (file)
@@ -32,12 +32,12 @@ import (
 
        "git.fd.io/govpp.git/adapter"
        "git.fd.io/govpp.git/codec"
-       "git.fd.io/govpp.git/examples/binapi/memclnt"
 )
 
 const (
        // DefaultSocketName is default VPP API socket file path.
        DefaultSocketName = adapter.DefaultBinapiSocket
+       legacySocketName  = "/run/vpp-api.sock"
 )
 
 var (
@@ -71,6 +71,27 @@ func init() {
        }
 }
 
+const socketMissing = `
+------------------------------------------------------------
+ No socket file found at: %s
+ VPP binary API socket file is missing!
+
+  - is VPP running with socket for binapi enabled?
+  - is the correct socket name configured?
+
+ To enable it add following section to your VPP config:
+   socksvr {
+     default
+   }
+------------------------------------------------------------
+`
+
+var warnOnce sync.Once
+
+func (c *vppClient) printMissingSocketMsg() {
+       fmt.Fprintf(os.Stderr, socketMissing, c.sockAddr)
+}
+
 type vppClient struct {
        sockAddr string
 
@@ -115,6 +136,32 @@ func (c *vppClient) SetDisconnectTimeout(t time.Duration) {
        c.disconnectTimeout = t
 }
 
+func (c *vppClient) SetMsgCallback(cb adapter.MsgCallback) {
+       Log.Debug("SetMsgCallback")
+       c.cb = cb
+}
+
+func (c *vppClient) checkLegacySocket() bool {
+       if c.sockAddr == legacySocketName {
+               return false
+       }
+       Log.Debugf("checking legacy socket: %s", legacySocketName)
+       // check if socket exists
+       if _, err := os.Stat(c.sockAddr); err == nil {
+               return false // socket exists
+       } else if !os.IsNotExist(err) {
+               return false // some other error occurred
+       }
+       // check if legacy socket exists
+       if _, err := os.Stat(legacySocketName); err == nil {
+               // legacy socket exists, update sockAddr
+               c.sockAddr = legacySocketName
+               return true
+       }
+       // no socket socket found
+       return false
+}
+
 // WaitReady checks socket file existence and waits for it if necessary
 func (c *vppClient) WaitReady() error {
        // check if socket already exists
@@ -124,6 +171,10 @@ func (c *vppClient) WaitReady() error {
                return err // some other error occurred
        }
 
+       if c.checkLegacySocket() {
+               return nil
+       }
+
        // socket does not exist, watch for it
        watcher, err := fsnotify.NewWatcher()
        if err != nil {
@@ -144,6 +195,9 @@ func (c *vppClient) WaitReady() error {
        for {
                select {
                case <-timeout.C:
+                       if c.checkLegacySocket() {
+                               return nil
+                       }
                        return fmt.Errorf("timeout waiting (%s) for socket file: %s", MaxWaitReady, c.sockAddr)
 
                case e := <-watcher.Errors:
@@ -159,13 +213,16 @@ func (c *vppClient) WaitReady() error {
        }
 }
 
-func (c *vppClient) SetMsgCallback(cb adapter.MsgCallback) {
-       Log.Debug("SetMsgCallback")
-       c.cb = cb
-}
-
 func (c *vppClient) Connect() error {
-       Log.Debugf("Connecting to: %v", c.sockAddr)
+       c.checkLegacySocket()
+
+       // check if socket exists
+       if _, err := os.Stat(c.sockAddr); os.IsNotExist(err) {
+               warnOnce.Do(c.printMissingSocketMsg)
+               return fmt.Errorf("VPP API socket file %s does not exist", c.sockAddr)
+       } else if err != nil {
+               return fmt.Errorf("VPP API socket error: %v", err)
+       }
 
        if err := c.connect(c.sockAddr); err != nil {
                return err
@@ -212,12 +269,14 @@ func (c *vppClient) Disconnect() error {
 func (c *vppClient) connect(sockAddr string) error {
        addr := &net.UnixAddr{Name: sockAddr, Net: "unix"}
 
+       Log.Debugf("Connecting to: %v", c.sockAddr)
+
        conn, err := net.DialUnix("unix", nil, addr)
        if err != nil {
                // we try different type of socket for backwards compatbility with VPP<=19.04
                if strings.Contains(err.Error(), "wrong type for socket") {
                        addr.Net = "unixpacket"
-                       Log.Warnf("%s, retrying connect with type unixpacket", err)
+                       Log.Debugf("%s, retrying connect with type unixpacket", err)
                        conn, err = net.DialUnix("unixpacket", nil, addr)
                }
                if err != nil {
@@ -253,9 +312,7 @@ const (
 func (c *vppClient) open() error {
        msgCodec := new(codec.MsgCodec)
 
-       req := &memclnt.SockclntCreate{
-               Name: []byte(ClientName),
-       }
+       req := &SockclntCreate{Name: ClientName}
        msg, err := msgCodec.EncodeMsg(req, sockCreateMsgId)
        if err != nil {
                Log.Debugln("Encode error:", err)
@@ -283,7 +340,7 @@ func (c *vppClient) open() error {
                return err
        }
 
-       reply := new(memclnt.SockclntCreateReply)
+       reply := new(SockclntCreateReply)
        if err := msgCodec.DecodeMsg(msgReply, reply); err != nil {
                Log.Println("Decode error:", err)
                return err
@@ -295,7 +352,8 @@ func (c *vppClient) open() error {
        c.clientIndex = reply.Index
        c.msgTable = make(map[string]uint16, reply.Count)
        for _, x := range reply.MessageTable {
-               name := string(bytes.TrimSuffix(bytes.Split(x.Name, []byte{0x00})[0], []byte{0x13}))
+               msgName := strings.Split(x.Name, "\x00")[0]
+               name := strings.TrimSuffix(msgName, "\x13")
                c.msgTable[name] = x.Index
                if strings.HasPrefix(name, "sockclnt_delete_") {
                        c.sockDelMsgId = x.Index
@@ -311,7 +369,7 @@ func (c *vppClient) open() error {
 func (c *vppClient) close() error {
        msgCodec := new(codec.MsgCodec)
 
-       req := &memclnt.SockclntDelete{
+       req := &SockclntDelete{
                Index: c.clientIndex,
        }
        msg, err := msgCodec.EncodeMsg(req, c.sockDelMsgId)
@@ -346,7 +404,7 @@ func (c *vppClient) close() error {
                return err
        }
 
-       reply := new(memclnt.SockclntDeleteReply)
+       reply := new(SockclntDeleteReply)
        if err := msgCodec.DecodeMsg(msgReply, reply); err != nil {
                Log.Debugln("Decode error:", err)
                return err
@@ -361,7 +419,7 @@ func (c *vppClient) GetMsgID(msgName string, msgCrc string) (uint16, error) {
        msg := msgName + "_" + msgCrc
        msgID, ok := c.msgTable[msg]
        if !ok {
-               return 0, fmt.Errorf("unknown message: %q", msg)
+               return 0, &adapter.UnknownMsgError{msgName, msgCrc}
        }
        return msgID, nil
 }