summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--adapter/socketclient/socketclient.go430
-rw-r--r--core/channel.go5
-rw-r--r--core/request_handler.go8
-rw-r--r--examples/bin_api/memclnt.api.json598
-rw-r--r--examples/bin_api/memclnt/memclnt.ba.go470
-rw-r--r--examples/perf-bench/perf-bench.go20
6 files changed, 1523 insertions, 8 deletions
diff --git a/adapter/socketclient/socketclient.go b/adapter/socketclient/socketclient.go
new file mode 100644
index 0000000..eec4fd0
--- /dev/null
+++ b/adapter/socketclient/socketclient.go
@@ -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")
+}
diff --git a/core/channel.go b/core/channel.go
index bf27b73..6cb02f7 100644
--- a/core/channel.go
+++ b/core/channel.go
@@ -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
}
diff --git a/core/request_handler.go b/core/request_handler.go
index 55a825a..fd8aa59 100644
--- a/core/request_handler.go
+++ b/core/request_handler.go
@@ -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
index 0000000..8014a26
--- /dev/null
+++ b/examples/bin_api/memclnt.api.json
@@ -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
index 0000000..68ff320
--- /dev/null
+++ b/examples/bin_api/memclnt/memclnt.ba.go
@@ -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),
+}
diff --git a/examples/perf-bench/perf-bench.go b/examples/perf-bench/perf-bench.go
index 24d3ebb..e5b0926 100644
--- a/examples/perf-bench/perf-bench.go
+++ b/examples/perf-bench/perf-bench.go
@@ -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) {