diff options
author | 2018-10-22 14:45:21 +0200 | |
---|---|---|
committer | 2018-10-22 14:56:09 +0200 | |
commit | 28df7c855784e784fb0e614c1cca0e565a7ef75f (patch) | |
tree | 7d2eb1b3522d57680df8a6f576356f9dd4dc706c /adapter/vppapiclient/vppapiclient.go | |
parent | 62d19032621c7db801b313a1e19e787cfb1fbc3e (diff) |
Introduce StatsAPI and it's initial implementation
- this implementation is basically Go wrapper around VPP's vppapiclient C library
Change-Id: I6f53dc3e228868834bf3a8a00c686ad05e22f3dd
Signed-off-by: Ondrej Fabry <ofabry@cisco.com>
Diffstat (limited to 'adapter/vppapiclient/vppapiclient.go')
-rw-r--r-- | adapter/vppapiclient/vppapiclient.go | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/adapter/vppapiclient/vppapiclient.go b/adapter/vppapiclient/vppapiclient.go new file mode 100644 index 0000000..34ad199 --- /dev/null +++ b/adapter/vppapiclient/vppapiclient.go @@ -0,0 +1,223 @@ +// Copyright (c) 2017 Cisco and/or its affiliates. +// +// 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. + +// +build !windows,!darwin + +package vppapiclient + +/* +#cgo CFLAGS: -DPNG_DEBUG=1 +#cgo LDFLAGS: -lvppapiclient + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <arpa/inet.h> +#include <vpp-api/client/vppapiclient.h> + +extern void go_msg_callback(uint16_t msg_id, void* data, size_t size); + +typedef struct __attribute__((__packed__)) _req_header { + uint16_t msg_id; + uint32_t client_index; + uint32_t context; +} req_header_t; + +typedef struct __attribute__((__packed__)) _reply_header { + uint16_t msg_id; +} reply_header_t; + +static void +govpp_msg_callback(unsigned char *data, int size) +{ + reply_header_t *header = ((reply_header_t *)data); + go_msg_callback(ntohs(header->msg_id), data, size); +} + +static int +govpp_send(uint32_t context, void *data, size_t size) +{ + req_header_t *header = ((req_header_t *)data); + header->context = htonl(context); + return vac_write(data, size); +} + +static int +govpp_connect(char *shm) +{ + return vac_connect("govpp", shm, govpp_msg_callback, 32); +} + +static int +govpp_disconnect() +{ + return vac_disconnect(); +} + +static uint32_t +govpp_get_msg_index(char *name_and_crc) +{ + return vac_get_msg_index(name_and_crc); +} +*/ +import "C" + +import ( + "fmt" + "os" + "path/filepath" + "reflect" + "unsafe" + + "git.fd.io/govpp.git/adapter" + "github.com/fsnotify/fsnotify" +) + +const ( + // shmDir is a directory where shared memory is supposed to be created. + shmDir = "/dev/shm/" + // vppShmFile is a default name of the file in the shmDir. + vppShmFile = "vpe-api" +) + +// global VPP binary API client adapter context +var client *VppClient + +// VppClient is the default implementation of the VppAPI. +type VppClient struct { + shmPrefix string + msgCallback adapter.MsgCallback +} + +// NewVppClient returns a new VPP binary API client. +func NewVppClient(shmPrefix string) *VppClient { + return &VppClient{ + shmPrefix: shmPrefix, + } +} + +// Connect connects the process to VPP. +func (a *VppClient) Connect() error { + if client != nil { + return fmt.Errorf("already connected to binary API, disconnect first") + } + + var rc _Ctype_int + if a.shmPrefix == "" { + rc = C.govpp_connect(nil) + } else { + shm := C.CString(a.shmPrefix) + rc = C.govpp_connect(shm) + } + if rc != 0 { + return fmt.Errorf("connecting to VPP binary API failed (rc=%v)", rc) + } + + client = a + return nil +} + +// Disconnect disconnects the process from VPP. +func (a *VppClient) Disconnect() error { + client = nil + + rc := C.govpp_disconnect() + if rc != 0 { + return fmt.Errorf("disconnecting from VPP binary API failed (rc=%v)", rc) + } + + return nil +} + +// GetMsgID returns a runtime message ID for the given message name and CRC. +func (a *VppClient) GetMsgID(msgName string, msgCrc string) (uint16, error) { + nameAndCrc := C.CString(msgName + "_" + msgCrc) + defer C.free(unsafe.Pointer(nameAndCrc)) + + msgID := uint16(C.govpp_get_msg_index(nameAndCrc)) + if msgID == ^uint16(0) { + // VPP does not know this message + return msgID, fmt.Errorf("unknown message: %v (crc: %v)", msgName, msgCrc) + } + + return msgID, nil +} + +// SendMsg sends a binary-encoded message to VPP. +func (a *VppClient) SendMsg(context uint32, data []byte) error { + rc := C.govpp_send(C.uint32_t(context), unsafe.Pointer(&data[0]), C.size_t(len(data))) + if rc != 0 { + return fmt.Errorf("unable to send the message (rc=%v)", rc) + } + return nil +} + +// SetMsgCallback sets a callback function that will be called by the adapter +// whenever a message comes from VPP. +func (a *VppClient) SetMsgCallback(cb adapter.MsgCallback) { + a.msgCallback = cb +} + +// WaitReady blocks until shared memory for sending +// binary api calls is present on the file system. +func (a *VppClient) WaitReady() error { + var path string + + // join the path to the shared memory segment + if a.shmPrefix == "" { + path = filepath.Join(shmDir, vppShmFile) + } else { + path = filepath.Join(shmDir, a.shmPrefix+"-"+vppShmFile) + } + + // check if file at the path exists + if _, err := os.Stat(path); err == nil { + // file exists, we are ready + return nil + } else if !os.IsNotExist(err) { + return err + } + + // file does not exist, start watching folder + watcher, err := fsnotify.NewWatcher() + if err != nil { + return err + } + defer watcher.Close() + + if err := watcher.Add(shmDir); err != nil { + return err + } + + for { + ev := <-watcher.Events + if ev.Name == path { + if (ev.Op & fsnotify.Create) == fsnotify.Create { + // file was created, we are ready + break + } + } + } + + return nil +} + +//export go_msg_callback +func go_msg_callback(msgID C.uint16_t, data unsafe.Pointer, size C.size_t) { + // convert unsafe.Pointer to byte slice + sliceHeader := &reflect.SliceHeader{Data: uintptr(data), Len: int(size), Cap: int(size)} + byteSlice := *(*[]byte)(unsafe.Pointer(sliceHeader)) + + client.msgCallback(uint16(msgID), byteSlice) +} |