Add socketclient implementation 35/18135/1
authorOndrej Fabry <ofabry@cisco.com>
Fri, 8 Mar 2019 10:18:22 +0000 (11:18 +0100)
committerOndrej Fabry <ofabry@cisco.com>
Fri, 8 Mar 2019 10:18:22 +0000 (11:18 +0100)
Change-Id: Ibf9edc0e5911d08229ac590b37c5afbc27f424a0
Signed-off-by: Ondrej Fabry <ofabry@cisco.com>
adapter/socketclient/socketclient.go [new file with mode: 0644]
core/channel.go
core/request_handler.go
examples/bin_api/memclnt.api.json [new file with mode: 0644]
examples/bin_api/memclnt/memclnt.ba.go [new file with mode: 0644]
examples/perf-bench/perf-bench.go

diff --git a/adapter/socketclient/socketclient.go b/adapter/socketclient/socketclient.go
new file mode 100644 (file)
index 0000000..eec4fd0
--- /dev/null
@@ -0,0 +1,430 @@
+package socketclient
+
+import (
+       "bufio"
+       "bytes"
+       "fmt"
+       "io"
+       "net"
+       "os"
+       "strings"
+       "sync"
+       "time"
+
+       "github.com/lunixbochs/struc"
+       logger "github.com/sirupsen/logrus"
+
+       "git.fd.io/govpp.git/adapter"
+       "git.fd.io/govpp.git/codec"
+       "git.fd.io/govpp.git/examples/bin_api/memclnt"
+)
+
+const (
+       // DefaultSocketName is default VPP API socket file name
+       DefaultSocketName = "/run/vpp-api.sock"
+
+       sockCreateMsgId = 15          // hard-coded id for sockclnt_create message
+       govppClientName = "govppsock" // client name used for socket registration
+)
+
+var (
+       ConnectTimeout    = time.Second * 3
+       DisconnectTimeout = time.Second
+
+       Debug       = os.Getenv("DEBUG_GOVPP_SOCK") != ""
+       DebugMsgIds = os.Getenv("DEBUG_GOVPP_SOCKMSG") != ""
+
+       Log = logger.New() // global logger
+)
+
+// init initializes global logger, which logs debug level messages to stdout.
+func init() {
+       Log.Out = os.Stdout
+       if Debug {
+               Log.Level = logger.DebugLevel
+       }
+}
+
+type vppClient struct {
+       sockAddr     string
+       conn         *net.UnixConn
+       reader       *bufio.Reader
+       cb           adapter.MsgCallback
+       clientIndex  uint32
+       msgTable     map[string]uint16
+       sockDelMsgId uint16
+       writeMu      sync.Mutex
+       quit         chan struct{}
+       wg           sync.WaitGroup
+}
+
+func NewVppClient(sockAddr string) *vppClient {
+       if sockAddr == "" {
+               sockAddr = DefaultSocketName
+       }
+       return &vppClient{
+               sockAddr: sockAddr,
+               cb:       nilCallback,
+       }
+}
+
+func nilCallback(msgID uint16, data []byte) {
+       Log.Warnf("no callback set, dropping message: ID=%v len=%d", msgID, len(data))
+}
+
+func (*vppClient) WaitReady() error {
+       // TODO: add watcher for socket file?
+       return nil
+}
+
+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)
+
+       if err := c.connect(c.sockAddr); err != nil {
+               return err
+       }
+
+       if err := c.open(); err != nil {
+               return err
+       }
+
+       c.quit = make(chan struct{})
+       c.wg.Add(1)
+       go c.readerLoop()
+
+       return nil
+}
+
+func (c *vppClient) connect(sockAddr string) error {
+       addr, err := net.ResolveUnixAddr("unixpacket", sockAddr)
+       if err != nil {
+               Log.Debugln("ResolveUnixAddr error:", err)
+               return err
+       }
+
+       conn, err := net.DialUnix("unixpacket", nil, addr)
+       if err != nil {
+               Log.Debugln("Dial error:", err)
+               return err
+       }
+
+       c.conn = conn
+       c.reader = bufio.NewReader(c.conn)
+
+       Log.Debugf("Connected to socket: %v", addr)
+
+       return nil
+}
+
+func (c *vppClient) open() error {
+       msgCodec := new(codec.MsgCodec)
+
+       req := &memclnt.SockclntCreate{
+               Name: []byte(govppClientName),
+       }
+       msg, err := msgCodec.EncodeMsg(req, sockCreateMsgId)
+       if err != nil {
+               Log.Debugln("Encode error:", err)
+               return err
+       }
+       // set non-0 context
+       msg[5] = 123
+
+       if err := c.write(msg); err != nil {
+               Log.Debugln("Write error: ", err)
+               return err
+       }
+
+       readDeadline := time.Now().Add(ConnectTimeout)
+       if err := c.conn.SetReadDeadline(readDeadline); err != nil {
+               return err
+       }
+       msgReply, err := c.read()
+       if err != nil {
+               Log.Println("Read error:", err)
+               return err
+       }
+       // reset read deadline
+       if err := c.conn.SetReadDeadline(time.Time{}); err != nil {
+               return err
+       }
+
+       //log.Printf("Client got (%d): % 0X", len(msgReply), msgReply)
+
+       reply := new(memclnt.SockclntCreateReply)
+       if err := msgCodec.DecodeMsg(msgReply, reply); err != nil {
+               Log.Println("Decode error:", err)
+               return err
+       }
+
+       Log.Debugf("SockclntCreateReply: Response=%v Index=%v Count=%v",
+               reply.Response, reply.Index, reply.Count)
+
+       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}))
+               c.msgTable[name] = x.Index
+               if strings.HasPrefix(name, "sockclnt_delete_") {
+                       c.sockDelMsgId = x.Index
+               }
+               if DebugMsgIds {
+                       Log.Debugf(" - %4d: %q", x.Index, name)
+               }
+       }
+
+       return nil
+}
+
+func (c *vppClient) Disconnect() error {
+       if c.conn == nil {
+               return nil
+       }
+       Log.Debugf("Disconnecting..")
+
+       close(c.quit)
+
+       // force readerLoop to timeout
+       if err := c.conn.SetReadDeadline(time.Now()); err != nil {
+               return err
+       }
+
+       // wait for readerLoop to return
+       c.wg.Wait()
+
+       if err := c.close(); err != nil {
+               return err
+       }
+
+       if err := c.conn.Close(); err != nil {
+               Log.Debugln("Close socket conn failed:", err)
+               return err
+       }
+
+       return nil
+}
+
+func (c *vppClient) close() error {
+       msgCodec := new(codec.MsgCodec)
+
+       req := &memclnt.SockclntDelete{
+               Index: c.clientIndex,
+       }
+       msg, err := msgCodec.EncodeMsg(req, c.sockDelMsgId)
+       if err != nil {
+               Log.Debugln("Encode error:", err)
+               return err
+       }
+       // set non-0 context
+       msg[5] = 124
+
+       Log.Debugf("sending socklntDel (%d byes): % 0X\n", len(msg), msg)
+       if err := c.write(msg); err != nil {
+               Log.Debugln("Write error: ", err)
+               return err
+       }
+
+       readDeadline := time.Now().Add(DisconnectTimeout)
+       if err := c.conn.SetReadDeadline(readDeadline); err != nil {
+               return err
+       }
+       msgReply, err := c.read()
+       if err != nil {
+               Log.Debugln("Read error:", err)
+               if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
+                       // we accept timeout for reply
+                       return nil
+               }
+               return err
+       }
+       // reset read deadline
+       if err := c.conn.SetReadDeadline(time.Time{}); err != nil {
+               return err
+       }
+
+       reply := new(memclnt.SockclntDeleteReply)
+       if err := msgCodec.DecodeMsg(msgReply, reply); err != nil {
+               Log.Debugln("Decode error:", err)
+               return err
+       }
+
+       Log.Debugf("SockclntDeleteReply: Response=%v", reply.Response)
+
+       return nil
+}
+
+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 msgID, nil
+}
+
+type reqHeader struct {
+       //MsgID uint16
+       ClientIndex uint32
+       Context     uint32
+}
+
+func (c *vppClient) SendMsg(context uint32, data []byte) error {
+       h := &reqHeader{
+               ClientIndex: c.clientIndex,
+               Context:     context,
+       }
+       buf := new(bytes.Buffer)
+       if err := struc.Pack(buf, h); err != nil {
+               return err
+       }
+       copy(data[2:], buf.Bytes())
+
+       Log.Debugf("SendMsg (%d) context=%v client=%d: data: % 02X", len(data), context, c.clientIndex, data)
+
+       if err := c.write(data); err != nil {
+               Log.Debugln("write error: ", err)
+               return err
+       }
+
+       return nil
+}
+
+func (c *vppClient) write(msg []byte) error {
+       h := &msgheader{
+               Data_len: uint32(len(msg)),
+       }
+       buf := new(bytes.Buffer)
+       if err := struc.Pack(buf, h); err != nil {
+               return err
+       }
+       header := buf.Bytes()
+
+       // we lock to prevent mixing multiple message sends
+       c.writeMu.Lock()
+       defer c.writeMu.Unlock()
+
+       var w io.Writer = c.conn
+
+       if n, err := w.Write(header); err != nil {
+               return err
+       } else {
+               Log.Debugf(" - header sent (%d/%d): % 0X", n, len(header), header)
+       }
+       if n, err := w.Write(msg); err != nil {
+               return err
+       } else {
+               Log.Debugf(" - msg sent (%d/%d): % 0X", n, len(msg), msg)
+       }
+
+       return nil
+}
+
+type msgHeader struct {
+       MsgID   uint16
+       Context uint32
+}
+
+func (c *vppClient) readerLoop() {
+       defer c.wg.Done()
+       for {
+               select {
+               case <-c.quit:
+                       Log.Debugf("reader quit")
+                       return
+               default:
+               }
+
+               msg, err := c.read()
+               if err != nil {
+                       if isClosedError(err) {
+                               return
+                       }
+                       Log.Debugf("READ FAILED: %v", err)
+                       continue
+               }
+               h := new(msgHeader)
+               if err := struc.Unpack(bytes.NewReader(msg), h); err != nil {
+                       Log.Debugf("unpacking header failed: %v", err)
+                       continue
+               }
+
+               Log.Debugf("recvMsg (%d) msgID=%d context=%v", len(msg), h.MsgID, h.Context)
+               c.cb(h.MsgID, msg)
+       }
+}
+
+type msgheader struct {
+       Q                 int    `struc:"uint64"`
+       Data_len          uint32 `struc:"uint32"`
+       Gc_mark_timestamp uint32 `struc:"uint32"`
+       //data              [0]uint8
+}
+
+func (c *vppClient) read() ([]byte, error) {
+       Log.Debug("reading next msg..")
+
+       header := make([]byte, 16)
+
+       n, err := io.ReadAtLeast(c.reader, header, 16)
+       if err != nil {
+               return nil, err
+       } else if n == 0 {
+               Log.Debugln("zero bytes header")
+               return nil, nil
+       }
+       if n != 16 {
+               Log.Debug("invalid header data (%d): % 0X", n, header[:n])
+               return nil, fmt.Errorf("invalid header (expected 16 bytes, got %d)", n)
+       }
+       Log.Debugf(" - read header %d bytes: % 0X", n, header)
+
+       h := &msgheader{}
+       if err := struc.Unpack(bytes.NewReader(header[:]), h); err != nil {
+               return nil, err
+       }
+       Log.Debugf(" - decoded header: %+v", h)
+
+       msgLen := int(h.Data_len)
+       msg := make([]byte, msgLen)
+
+       n, err = c.reader.Read(msg)
+       if err != nil {
+               return nil, err
+       }
+       Log.Debugf(" - read msg %d bytes (%d buffered)", n, c.reader.Buffered())
+
+       if msgLen > n {
+               remain := msgLen - n
+               Log.Debugf("continue read for another %d bytes", remain)
+               view := msg[n:]
+
+               for remain > 0 {
+
+                       nbytes, err := c.reader.Read(view)
+                       if err != nil {
+                               return nil, err
+                       } else if nbytes == 0 {
+                               return nil, fmt.Errorf("zero nbytes")
+                       }
+
+                       remain -= nbytes
+                       Log.Debugf("another data received: %d bytes (remain: %d)", nbytes, remain)
+
+                       view = view[nbytes:]
+               }
+       }
+
+       return msg, nil
+}
+
+func isClosedError(err error) bool {
+       if err == io.EOF {
+               return true
+       }
+       return strings.HasSuffix(err.Error(), "use of closed network connection")
+}
index bf27b73..6cb02f7 100644 (file)
@@ -258,6 +258,7 @@ func (ch *Channel) receiveReplyInternal(msg api.Message, expSeqNum uint16) (last
                case vppReply := <-ch.replyChan:
                        ignore, lastReplyReceived, err = ch.processReply(vppReply, expSeqNum, msg)
                        if ignore {
+                               logrus.Warnf("ignoring reply: %+v", vppReply)
                                continue
                        }
                        return lastReplyReceived, err
@@ -275,8 +276,8 @@ func (ch *Channel) processReply(reply *vppReply, expSeqNum uint16, msg api.Messa
        cmpSeqNums := compareSeqNumbers(reply.seqNum, expSeqNum)
        if cmpSeqNums == -1 {
                // reply received too late, ignore the message
-               logrus.WithField("seqNum", reply.seqNum).Warn(
-                       "Received reply to an already closed binary API request")
+               logrus.WithField("seqNum", reply.seqNum).
+                       Warn("Received reply to an already closed binary API request")
                ignore = true
                return
        }
index 55a825a..fd8aa59 100644 (file)
@@ -23,6 +23,8 @@ import (
        logger "github.com/sirupsen/logrus"
 )
 
+var ReplyChannelTimeout = time.Millisecond * 100
+
 var (
        ErrNotConnected = errors.New("not connected to VPP, ignoring the request")
        ErrProbeTimeout = errors.New("probe reply not received within timeout period")
@@ -91,7 +93,7 @@ func (c *Connection) processRequest(ch *Channel, req *vppRequest) error {
                        "msg_size": len(data),
                        "seq_num":  req.seqNum,
                        "msg_crc":  req.msg.GetCrcString(),
-               }).Debugf(" --> sending msg: %s", req.msg.GetMessageName())
+               }).Debugf("--> govpp send: %s: %+v", req.msg.GetMessageName(), req.msg)
        }
 
        // send the request to VPP
@@ -163,7 +165,7 @@ func (c *Connection) msgCallback(msgID uint16, data []byte) {
                        "is_multi": isMulti,
                        "seq_num":  seqNum,
                        "msg_crc":  msg.GetCrcString(),
-               }).Debugf(" <- received msg: %s", msg.GetMessageName())
+               }).Debugf("<-- govpp recv: %s", msg.GetMessageName())
        }
 
        if context == 0 || c.isNotificationMessage(msgID) {
@@ -209,7 +211,7 @@ func sendReply(ch *Channel, reply *vppReply) {
        select {
        case ch.replyChan <- reply:
                // reply sent successfully
-       case <-time.After(time.Millisecond * 100):
+       case <-time.After(ReplyChannelTimeout):
                // receiver still not ready
                log.WithFields(logger.Fields{
                        "channel": ch,
diff --git a/examples/bin_api/memclnt.api.json b/examples/bin_api/memclnt.api.json
new file mode 100644 (file)
index 0000000..8014a26
--- /dev/null
@@ -0,0 +1,598 @@
+{
+    "messages": [
+        [
+            "memclnt_create",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "i32",
+                "ctx_quota"
+            ],
+            [
+                "u64",
+                "input_queue"
+            ],
+            [
+                "u8",
+                "name",
+                64
+            ],
+            [
+                "u32",
+                "api_versions",
+                8
+            ],
+            {
+                "crc": "0x6d33c5ea"
+            }
+        ],
+        [
+            "memclnt_create_reply",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "i32",
+                "response"
+            ],
+            [
+                "u64",
+                "handle"
+            ],
+            [
+                "u32",
+                "index"
+            ],
+            [
+                "u64",
+                "message_table"
+            ],
+            {
+                "crc": "0x42ec4560"
+            }
+        ],
+        [
+            "memclnt_delete",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "index"
+            ],
+            [
+                "u64",
+                "handle"
+            ],
+            [
+                "u8",
+                "do_cleanup"
+            ],
+            {
+                "crc": "0x4dd351e9"
+            }
+        ],
+        [
+            "memclnt_delete_reply",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "i32",
+                "response"
+            ],
+            [
+                "u64",
+                "handle"
+            ],
+            {
+                "crc": "0x3d3b6312"
+            }
+        ],
+        [
+            "rx_thread_exit",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u8",
+                "dummy"
+            ],
+            {
+                "crc": "0xc3a3a452"
+            }
+        ],
+        [
+            "memclnt_rx_thread_suspend",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u8",
+                "dummy"
+            ],
+            {
+                "crc": "0xc3a3a452"
+            }
+        ],
+        [
+            "memclnt_read_timeout",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u8",
+                "dummy"
+            ],
+            {
+                "crc": "0xc3a3a452"
+            }
+        ],
+        [
+            "rpc_call",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "client_index"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "u64",
+                "function"
+            ],
+            [
+                "u8",
+                "multicast"
+            ],
+            [
+                "u8",
+                "need_barrier_sync"
+            ],
+            [
+                "u8",
+                "send_reply"
+            ],
+            [
+                "u32",
+                "data_len"
+            ],
+            [
+                "u8",
+                "data",
+                0,
+                "data_len"
+            ],
+            {
+                "crc": "0x7e8a2c95"
+            }
+        ],
+        [
+            "rpc_call_reply",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "i32",
+                "retval"
+            ],
+            {
+                "crc": "0xe8d4e804"
+            }
+        ],
+        [
+            "get_first_msg_id",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "client_index"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "u8",
+                "name",
+                64
+            ],
+            {
+                "crc": "0x0cb71b0e"
+            }
+        ],
+        [
+            "get_first_msg_id_reply",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "i32",
+                "retval"
+            ],
+            [
+                "u16",
+                "first_msg_id"
+            ],
+            {
+                "crc": "0x7d337472"
+            }
+        ],
+        [
+            "api_versions",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "client_index"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            {
+                "crc": "0x51077d14"
+            }
+        ],
+        [
+            "api_versions_reply",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "i32",
+                "retval"
+            ],
+            [
+                "u32",
+                "count"
+            ],
+            [
+                "vl_api_module_version_t",
+                "api_versions",
+                0,
+                "count"
+            ],
+            {
+                "crc": "0x90a39195"
+            }
+        ],
+        [
+            "trace_plugin_msg_ids",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "client_index"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "u8",
+                "plugin_name",
+                128
+            ],
+            [
+                "u16",
+                "first_msg_id"
+            ],
+            [
+                "u16",
+                "last_msg_id"
+            ],
+            {
+                "crc": "0x64af79f9"
+            }
+        ],
+        [
+            "sockclnt_create",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "u8",
+                "name",
+                64
+            ],
+            {
+                "crc": "0xdf2cf94d"
+            }
+        ],
+        [
+            "sockclnt_create_reply",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "client_index"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "i32",
+                "response"
+            ],
+            [
+                "u32",
+                "index"
+            ],
+            [
+                "u16",
+                "count"
+            ],
+            [
+                "vl_api_message_table_entry_t",
+                "message_table",
+                0,
+                "count"
+            ],
+            {
+                "crc": "0xa134a8a8"
+            }
+        ],
+        [
+            "sockclnt_delete",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "client_index"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "u32",
+                "index"
+            ],
+            {
+                "crc": "0x8ac76db6"
+            }
+        ],
+        [
+            "sockclnt_delete_reply",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "i32",
+                "response"
+            ],
+            {
+                "crc": "0x8f38b1ee"
+            }
+        ],
+        [
+            "sock_init_shm",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "client_index"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "u32",
+                "requested_size"
+            ],
+            [
+                "u8",
+                "nitems"
+            ],
+            [
+                "u64",
+                "configs",
+                0,
+                "nitems"
+            ],
+            {
+                "crc": "0x51646d92"
+            }
+        ],
+        [
+            "sock_init_shm_reply",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "i32",
+                "retval"
+            ],
+            {
+                "crc": "0xe8d4e804"
+            }
+        ],
+        [
+            "memclnt_keepalive",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "client_index"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            {
+                "crc": "0x51077d14"
+            }
+        ],
+        [
+            "memclnt_keepalive_reply",
+            [
+                "u16",
+                "_vl_msg_id"
+            ],
+            [
+                "u32",
+                "context"
+            ],
+            [
+                "i32",
+                "retval"
+            ],
+            {
+                "crc": "0xe8d4e804"
+            }
+        ]
+    ],
+    "vl_api_version": "0xb619530",
+    "unions": [],
+    "services": {
+        "api_versions": {
+            "reply": "api_versions_reply"
+        },
+        "memclnt_keepalive": {
+            "reply": "memclnt_keepalive_reply"
+        },
+        "memclnt_rx_thread_suspend": {
+            "reply": "null"
+        },
+        "sockclnt_delete": {
+            "reply": "sockclnt_delete_reply"
+        },
+        "memclnt_create": {
+            "reply": "memclnt_create_reply"
+        },
+        "get_first_msg_id": {
+            "reply": "get_first_msg_id_reply"
+        },
+        "memclnt_read_timeout": {
+            "reply": "null"
+        },
+        "rpc_call": {
+            "reply": "rpc_call_reply"
+        },
+        "rx_thread_exit": {
+            "reply": "null"
+        },
+        "sock_init_shm": {
+            "reply": "sock_init_shm_reply"
+        },
+        "memclnt_delete": {
+            "reply": "memclnt_delete_reply"
+        },
+        "sockclnt_create": {
+            "reply": "sockclnt_create_reply"
+        },
+        "trace_plugin_msg_ids": {
+            "reply": "null"
+        }
+    },
+    "enums": [],
+    "types": [
+        [
+            "module_version",
+            [
+                "u32",
+                "major"
+            ],
+            [
+                "u32",
+                "minor"
+            ],
+            [
+                "u32",
+                "patch"
+            ],
+            [
+                "u8",
+                "name",
+                64
+            ],
+            {
+                "crc": "0x4b6da11a"
+            }
+        ],
+        [
+            "message_table_entry",
+            [
+                "u16",
+                "index"
+            ],
+            [
+                "u8",
+                "name",
+                64
+            ],
+            {
+                "crc": "0x913bf1c6"
+            }
+        ]
+    ],
+    "aliases": {}
+}
diff --git a/examples/bin_api/memclnt/memclnt.ba.go b/examples/bin_api/memclnt/memclnt.ba.go
new file mode 100644 (file)
index 0000000..68ff320
--- /dev/null
@@ -0,0 +1,470 @@
+// Code generated by GoVPP binapi-generator. DO NOT EDIT.
+//  source: memclnt.api.json
+
+/*
+ Package memclnt is a generated from VPP binary API module 'memclnt'.
+
+ It contains following objects:
+        13 services
+         2 types
+        22 messages
+*/
+package memclnt
+
+import api "git.fd.io/govpp.git/api"
+import struc "github.com/lunixbochs/struc"
+import bytes "bytes"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = api.RegisterMessage
+var _ = struc.Pack
+var _ = bytes.NewBuffer
+
+// Services represents VPP binary API services:
+type Services interface {
+       APIVersions(*APIVersions) (*APIVersionsReply, error)
+       GetFirstMsgID(*GetFirstMsgID) (*GetFirstMsgIDReply, error)
+       MemclntCreate(*MemclntCreate) (*MemclntCreateReply, error)
+       MemclntDelete(*MemclntDelete) (*MemclntDeleteReply, error)
+       MemclntKeepalive(*MemclntKeepalive) (*MemclntKeepaliveReply, error)
+       MemclntReadTimeout(*MemclntReadTimeout) error
+       MemclntRxThreadSuspend(*MemclntRxThreadSuspend) error
+       RPCCall(*RPCCall) (*RPCCallReply, error)
+       RxThreadExit(*RxThreadExit) error
+       SockInitShm(*SockInitShm) (*SockInitShmReply, error)
+       SockclntCreate(*SockclntCreate) (*SockclntCreateReply, error)
+       SockclntDelete(*SockclntDelete) (*SockclntDeleteReply, error)
+       TracePluginMsgIds(*TracePluginMsgIds) error
+}
+
+/* Types */
+
+// MessageTableEntry represents VPP binary API type 'message_table_entry':
+type MessageTableEntry struct {
+       Index uint16
+       Name  []byte `struc:"[64]byte"`
+}
+
+func (*MessageTableEntry) GetTypeName() string {
+       return "message_table_entry"
+}
+func (*MessageTableEntry) GetCrcString() string {
+       return "913bf1c6"
+}
+
+// ModuleVersion represents VPP binary API type 'module_version':
+type ModuleVersion struct {
+       Major uint32
+       Minor uint32
+       Patch uint32
+       Name  []byte `struc:"[64]byte"`
+}
+
+func (*ModuleVersion) GetTypeName() string {
+       return "module_version"
+}
+func (*ModuleVersion) GetCrcString() string {
+       return "4b6da11a"
+}
+
+/* Messages */
+
+// APIVersions represents VPP binary API message 'api_versions':
+type APIVersions struct{}
+
+func (*APIVersions) GetMessageName() string {
+       return "api_versions"
+}
+func (*APIVersions) GetCrcString() string {
+       return "51077d14"
+}
+func (*APIVersions) GetMessageType() api.MessageType {
+       return api.RequestMessage
+}
+
+// APIVersionsReply represents VPP binary API message 'api_versions_reply':
+type APIVersionsReply struct {
+       Retval      int32
+       Count       uint32 `struc:"sizeof=APIVersions"`
+       APIVersions []ModuleVersion
+}
+
+func (*APIVersionsReply) GetMessageName() string {
+       return "api_versions_reply"
+}
+func (*APIVersionsReply) GetCrcString() string {
+       return "90a39195"
+}
+func (*APIVersionsReply) GetMessageType() api.MessageType {
+       return api.ReplyMessage
+}
+
+// GetFirstMsgID represents VPP binary API message 'get_first_msg_id':
+type GetFirstMsgID struct {
+       Name []byte `struc:"[64]byte"`
+}
+
+func (*GetFirstMsgID) GetMessageName() string {
+       return "get_first_msg_id"
+}
+func (*GetFirstMsgID) GetCrcString() string {
+       return "0cb71b0e"
+}
+func (*GetFirstMsgID) GetMessageType() api.MessageType {
+       return api.RequestMessage
+}
+
+// GetFirstMsgIDReply represents VPP binary API message 'get_first_msg_id_reply':
+type GetFirstMsgIDReply struct {
+       Retval     int32
+       FirstMsgID uint16
+}
+
+func (*GetFirstMsgIDReply) GetMessageName() string {
+       return "get_first_msg_id_reply"
+}
+func (*GetFirstMsgIDReply) GetCrcString() string {
+       return "7d337472"
+}
+func (*GetFirstMsgIDReply) GetMessageType() api.MessageType {
+       return api.ReplyMessage
+}
+
+// MemclntCreate represents VPP binary API message 'memclnt_create':
+type MemclntCreate struct {
+       CtxQuota    int32
+       InputQueue  uint64
+       Name        []byte   `struc:"[64]byte"`
+       APIVersions []uint32 `struc:"[8]uint32"`
+}
+
+func (*MemclntCreate) GetMessageName() string {
+       return "memclnt_create"
+}
+func (*MemclntCreate) GetCrcString() string {
+       return "6d33c5ea"
+}
+func (*MemclntCreate) GetMessageType() api.MessageType {
+       return api.ReplyMessage
+}
+
+// MemclntCreateReply represents VPP binary API message 'memclnt_create_reply':
+type MemclntCreateReply struct {
+       Response     int32
+       Handle       uint64
+       Index        uint32
+       MessageTable uint64
+}
+
+func (*MemclntCreateReply) GetMessageName() string {
+       return "memclnt_create_reply"
+}
+func (*MemclntCreateReply) GetCrcString() string {
+       return "42ec4560"
+}
+func (*MemclntCreateReply) GetMessageType() api.MessageType {
+       return api.ReplyMessage
+}
+
+// MemclntDelete represents VPP binary API message 'memclnt_delete':
+type MemclntDelete struct {
+       Index     uint32
+       Handle    uint64
+       DoCleanup uint8
+}
+
+func (*MemclntDelete) GetMessageName() string {
+       return "memclnt_delete"
+}
+func (*MemclntDelete) GetCrcString() string {
+       return "4dd351e9"
+}
+func (*MemclntDelete) GetMessageType() api.MessageType {
+       return api.OtherMessage
+}
+
+// MemclntDeleteReply represents VPP binary API message 'memclnt_delete_reply':
+type MemclntDeleteReply struct {
+       Response int32
+       Handle   uint64
+}
+
+func (*MemclntDeleteReply) GetMessageName() string {
+       return "memclnt_delete_reply"
+}
+func (*MemclntDeleteReply) GetCrcString() string {
+       return "3d3b6312"
+}
+func (*MemclntDeleteReply) GetMessageType() api.MessageType {
+       return api.OtherMessage
+}
+
+// MemclntKeepalive represents VPP binary API message 'memclnt_keepalive':
+type MemclntKeepalive struct{}
+
+func (*MemclntKeepalive) GetMessageName() string {
+       return "memclnt_keepalive"
+}
+func (*MemclntKeepalive) GetCrcString() string {
+       return "51077d14"
+}
+func (*MemclntKeepalive) GetMessageType() api.MessageType {
+       return api.RequestMessage
+}
+
+// MemclntKeepaliveReply represents VPP binary API message 'memclnt_keepalive_reply':
+type MemclntKeepaliveReply struct {
+       Retval int32
+}
+
+func (*MemclntKeepaliveReply) GetMessageName() string {
+       return "memclnt_keepalive_reply"
+}
+func (*MemclntKeepaliveReply) GetCrcString() string {
+       return "e8d4e804"
+}
+func (*MemclntKeepaliveReply) GetMessageType() api.MessageType {
+       return api.ReplyMessage
+}
+
+// MemclntReadTimeout represents VPP binary API message 'memclnt_read_timeout':
+type MemclntReadTimeout struct {
+       Dummy uint8
+}
+
+func (*MemclntReadTimeout) GetMessageName() string {
+       return "memclnt_read_timeout"
+}
+func (*MemclntReadTimeout) GetCrcString() string {
+       return "c3a3a452"
+}
+func (*MemclntReadTimeout) GetMessageType() api.MessageType {
+       return api.OtherMessage
+}
+
+// MemclntRxThreadSuspend represents VPP binary API message 'memclnt_rx_thread_suspend':
+type MemclntRxThreadSuspend struct {
+       Dummy uint8
+}
+
+func (*MemclntRxThreadSuspend) GetMessageName() string {
+       return "memclnt_rx_thread_suspend"
+}
+func (*MemclntRxThreadSuspend) GetCrcString() string {
+       return "c3a3a452"
+}
+func (*MemclntRxThreadSuspend) GetMessageType() api.MessageType {
+       return api.OtherMessage
+}
+
+// RPCCall represents VPP binary API message 'rpc_call':
+type RPCCall struct {
+       Function        uint64
+       Multicast       uint8
+       NeedBarrierSync uint8
+       SendReply       uint8
+       DataLen         uint32 `struc:"sizeof=Data"`
+       Data            []byte
+}
+
+func (*RPCCall) GetMessageName() string {
+       return "rpc_call"
+}
+func (*RPCCall) GetCrcString() string {
+       return "7e8a2c95"
+}
+func (*RPCCall) GetMessageType() api.MessageType {
+       return api.RequestMessage
+}
+
+// RPCCallReply represents VPP binary API message 'rpc_call_reply':
+type RPCCallReply struct {
+       Retval int32
+}
+
+func (*RPCCallReply) GetMessageName() string {
+       return "rpc_call_reply"
+}
+func (*RPCCallReply) GetCrcString() string {
+       return "e8d4e804"
+}
+func (*RPCCallReply) GetMessageType() api.MessageType {
+       return api.ReplyMessage
+}
+
+// RxThreadExit represents VPP binary API message 'rx_thread_exit':
+type RxThreadExit struct {
+       Dummy uint8
+}
+
+func (*RxThreadExit) GetMessageName() string {
+       return "rx_thread_exit"
+}
+func (*RxThreadExit) GetCrcString() string {
+       return "c3a3a452"
+}
+func (*RxThreadExit) GetMessageType() api.MessageType {
+       return api.OtherMessage
+}
+
+// SockInitShm represents VPP binary API message 'sock_init_shm':
+type SockInitShm struct {
+       RequestedSize uint32
+       Nitems        uint8 `struc:"sizeof=Configs"`
+       Configs       []uint64
+}
+
+func (*SockInitShm) GetMessageName() string {
+       return "sock_init_shm"
+}
+func (*SockInitShm) GetCrcString() string {
+       return "51646d92"
+}
+func (*SockInitShm) GetMessageType() api.MessageType {
+       return api.RequestMessage
+}
+
+// SockInitShmReply represents VPP binary API message 'sock_init_shm_reply':
+type SockInitShmReply struct {
+       Retval int32
+}
+
+func (*SockInitShmReply) GetMessageName() string {
+       return "sock_init_shm_reply"
+}
+func (*SockInitShmReply) GetCrcString() string {
+       return "e8d4e804"
+}
+func (*SockInitShmReply) GetMessageType() api.MessageType {
+       return api.ReplyMessage
+}
+
+// SockclntCreate represents VPP binary API message 'sockclnt_create':
+type SockclntCreate struct {
+       Name []byte `struc:"[64]byte"`
+}
+
+func (*SockclntCreate) GetMessageName() string {
+       return "sockclnt_create"
+}
+func (*SockclntCreate) GetCrcString() string {
+       return "df2cf94d"
+}
+func (*SockclntCreate) GetMessageType() api.MessageType {
+       return api.ReplyMessage
+}
+
+// SockclntCreateReply represents VPP binary API message 'sockclnt_create_reply':
+type SockclntCreateReply struct {
+       Response     int32
+       Index        uint32
+       Count        uint16 `struc:"sizeof=MessageTable"`
+       MessageTable []MessageTableEntry
+}
+
+func (*SockclntCreateReply) GetMessageName() string {
+       return "sockclnt_create_reply"
+}
+func (*SockclntCreateReply) GetCrcString() string {
+       return "a134a8a8"
+}
+func (*SockclntCreateReply) GetMessageType() api.MessageType {
+       return api.RequestMessage
+}
+
+// SockclntDelete represents VPP binary API message 'sockclnt_delete':
+type SockclntDelete struct {
+       Index uint32
+}
+
+func (*SockclntDelete) GetMessageName() string {
+       return "sockclnt_delete"
+}
+func (*SockclntDelete) GetCrcString() string {
+       return "8ac76db6"
+}
+func (*SockclntDelete) GetMessageType() api.MessageType {
+       return api.RequestMessage
+}
+
+// SockclntDeleteReply represents VPP binary API message 'sockclnt_delete_reply':
+type SockclntDeleteReply struct {
+       Response int32
+}
+
+func (*SockclntDeleteReply) GetMessageName() string {
+       return "sockclnt_delete_reply"
+}
+func (*SockclntDeleteReply) GetCrcString() string {
+       return "8f38b1ee"
+}
+func (*SockclntDeleteReply) GetMessageType() api.MessageType {
+       return api.ReplyMessage
+}
+
+// TracePluginMsgIds represents VPP binary API message 'trace_plugin_msg_ids':
+type TracePluginMsgIds struct {
+       PluginName []byte `struc:"[128]byte"`
+       FirstMsgID uint16
+       LastMsgID  uint16
+}
+
+func (*TracePluginMsgIds) GetMessageName() string {
+       return "trace_plugin_msg_ids"
+}
+func (*TracePluginMsgIds) GetCrcString() string {
+       return "64af79f9"
+}
+func (*TracePluginMsgIds) GetMessageType() api.MessageType {
+       return api.RequestMessage
+}
+
+func init() {
+       api.RegisterMessage((*APIVersions)(nil), "memclnt.APIVersions")
+       api.RegisterMessage((*APIVersionsReply)(nil), "memclnt.APIVersionsReply")
+       api.RegisterMessage((*GetFirstMsgID)(nil), "memclnt.GetFirstMsgID")
+       api.RegisterMessage((*GetFirstMsgIDReply)(nil), "memclnt.GetFirstMsgIDReply")
+       api.RegisterMessage((*MemclntCreate)(nil), "memclnt.MemclntCreate")
+       api.RegisterMessage((*MemclntCreateReply)(nil), "memclnt.MemclntCreateReply")
+       api.RegisterMessage((*MemclntDelete)(nil), "memclnt.MemclntDelete")
+       api.RegisterMessage((*MemclntDeleteReply)(nil), "memclnt.MemclntDeleteReply")
+       api.RegisterMessage((*MemclntKeepalive)(nil), "memclnt.MemclntKeepalive")
+       api.RegisterMessage((*MemclntKeepaliveReply)(nil), "memclnt.MemclntKeepaliveReply")
+       api.RegisterMessage((*MemclntReadTimeout)(nil), "memclnt.MemclntReadTimeout")
+       api.RegisterMessage((*MemclntRxThreadSuspend)(nil), "memclnt.MemclntRxThreadSuspend")
+       api.RegisterMessage((*RPCCall)(nil), "memclnt.RPCCall")
+       api.RegisterMessage((*RPCCallReply)(nil), "memclnt.RPCCallReply")
+       api.RegisterMessage((*RxThreadExit)(nil), "memclnt.RxThreadExit")
+       api.RegisterMessage((*SockInitShm)(nil), "memclnt.SockInitShm")
+       api.RegisterMessage((*SockInitShmReply)(nil), "memclnt.SockInitShmReply")
+       api.RegisterMessage((*SockclntCreate)(nil), "memclnt.SockclntCreate")
+       api.RegisterMessage((*SockclntCreateReply)(nil), "memclnt.SockclntCreateReply")
+       api.RegisterMessage((*SockclntDelete)(nil), "memclnt.SockclntDelete")
+       api.RegisterMessage((*SockclntDeleteReply)(nil), "memclnt.SockclntDeleteReply")
+       api.RegisterMessage((*TracePluginMsgIds)(nil), "memclnt.TracePluginMsgIds")
+}
+
+var Messages = []api.Message{
+       (*APIVersions)(nil),
+       (*APIVersionsReply)(nil),
+       (*GetFirstMsgID)(nil),
+       (*GetFirstMsgIDReply)(nil),
+       (*MemclntCreate)(nil),
+       (*MemclntCreateReply)(nil),
+       (*MemclntDelete)(nil),
+       (*MemclntDeleteReply)(nil),
+       (*MemclntKeepalive)(nil),
+       (*MemclntKeepaliveReply)(nil),
+       (*MemclntReadTimeout)(nil),
+       (*MemclntRxThreadSuspend)(nil),
+       (*RPCCall)(nil),
+       (*RPCCallReply)(nil),
+       (*RxThreadExit)(nil),
+       (*SockInitShm)(nil),
+       (*SockInitShmReply)(nil),
+       (*SockclntCreate)(nil),
+       (*SockclntCreateReply)(nil),
+       (*SockclntDelete)(nil),
+       (*SockclntDeleteReply)(nil),
+       (*TracePluginMsgIds)(nil),
+}
index 24d3ebb..e5b0926 100644 (file)
@@ -25,7 +25,9 @@ import (
        "github.com/pkg/profile"
        "github.com/sirupsen/logrus"
 
-       "git.fd.io/govpp.git"
+       "git.fd.io/govpp.git/adapter"
+       "git.fd.io/govpp.git/adapter/socketclient"
+       "git.fd.io/govpp.git/adapter/vppapiclient"
        "git.fd.io/govpp.git/api"
        "git.fd.io/govpp.git/core"
        "git.fd.io/govpp.git/examples/bin_api/vpe"
@@ -38,9 +40,10 @@ const (
 
 func main() {
        // parse optional flags
-       var sync, prof bool
+       var sync, prof, sock bool
        var cnt int
        flag.BoolVar(&sync, "sync", false, "run synchronous perf test")
+       flag.BoolVar(&sock, "sock", false, "use socket client for VPP API")
        flag.IntVar(&cnt, "count", 0, "count of requests to be sent to VPP")
        flag.BoolVar(&prof, "prof", false, "generate profile data")
        flag.Parse()
@@ -58,8 +61,15 @@ func main() {
                defer profile.Start().Stop()
        }
 
+       var a adapter.VppAPI
+       if sock {
+               a = socketclient.NewVppClient("/run/vpp-api.sock")
+       } else {
+               a = vppapiclient.NewVppClient("")
+       }
+
        // connect to VPP
-       conn, err := govpp.Connect("")
+       conn, err := core.Connect(a)
        if err != nil {
                log.Fatalln("Error:", err)
        }
@@ -72,6 +82,8 @@ func main() {
        }
        defer ch.Close()
 
+       ch.SetReplyTimeout(time.Second * 2)
+
        // log only errors
        core.SetLogger(&logrus.Logger{Level: logrus.ErrorLevel})
 
@@ -89,6 +101,8 @@ func main() {
        elapsed := time.Since(start)
        fmt.Println("Test took:", elapsed)
        fmt.Printf("Requests per second: %.0f\n", float64(cnt)/elapsed.Seconds())
+
+       time.Sleep(time.Second)
 }
 
 func syncTest(ch api.Channel, cnt int) {