aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOndrej Fabry <ofabry@cisco.com>2018-10-22 14:21:28 +0000
committerGerrit Code Review <gerrit@fd.io>2018-10-22 14:21:28 +0000
commitc00356ec332203f353fcd5f5992226940d90da92 (patch)
treef848f4b40300982c913d17eda3feb52b6ef3c697
parent56e997fc27c775790fcce5eda92bb2baee82d881 (diff)
parent28df7c855784e784fb0e614c1cca0e565a7ef75f (diff)
Merge "Introduce StatsAPI and it's initial implementation"
-rw-r--r--.gitignore1
-rw-r--r--adapter/mock/mock_adapter.go6
-rw-r--r--adapter/stats_api.go86
-rw-r--r--adapter/vpp_api.go (renamed from adapter/adapter.go)9
-rw-r--r--adapter/vppapiclient/doc.go18
-rw-r--r--adapter/vppapiclient/stat_client.go279
-rw-r--r--adapter/vppapiclient/stat_client_stub.go45
-rw-r--r--adapter/vppapiclient/vppapiclient.go (renamed from adapter/vppapiclient/vppapiclient_adapter.go)131
-rw-r--r--adapter/vppapiclient/vppapiclient_stub.go (renamed from adapter/vppapiclient/empty_adapter.go)29
-rw-r--r--core/connection.go45
-rw-r--r--core/request_handler.go6
-rw-r--r--examples/cmd/stats-api/stats_api.go66
-rw-r--r--examples/cmd/stats-client/stats_client.go9
-rw-r--r--govpp.go32
14 files changed, 640 insertions, 122 deletions
diff --git a/.gitignore b/.gitignore
index c10a9ca..7ec04f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@ cmd/binapi-generator/binapi-generator
examples/cmd/perf-bench/perf-bench
examples/cmd/simple-client/simple-client
examples/cmd/stats-client/stats-client
+examples/cmd/stats-api/stats-api
# extras
extras/libmemif/examples/gopacket/gopacket
diff --git a/adapter/mock/mock_adapter.go b/adapter/mock/mock_adapter.go
index cdf2081..9783651 100644
--- a/adapter/mock/mock_adapter.go
+++ b/adapter/mock/mock_adapter.go
@@ -37,7 +37,7 @@ const (
useReplyHandlers // use reply handler
)
-// VppAdapter represents a mock VPP adapter that can be used for unit/integration testing instead of the vppapiclient adapter.
+// VppAPI represents a mock VPP adapter that can be used for unit/integration testing instead of the vppapiclient adapter.
type VppAdapter struct {
callback adapter.MsgCallback
@@ -107,8 +107,8 @@ func (a *VppAdapter) Connect() error {
}
// Disconnect emulates disconnecting the process from VPP.
-func (a *VppAdapter) Disconnect() {
- // no op
+func (a *VppAdapter) Disconnect() error {
+ return nil
}
// GetMsgNameByID returns message name for specified message ID.
diff --git a/adapter/stats_api.go b/adapter/stats_api.go
new file mode 100644
index 0000000..8815aae
--- /dev/null
+++ b/adapter/stats_api.go
@@ -0,0 +1,86 @@
+package adapter
+
+// StatsAPI provides connection to VPP stats API.
+type StatsAPI interface {
+ // Connect establishes client connection to the stats API.
+ Connect() error
+
+ // Disconnect terminates client connection.
+ Disconnect() error
+
+ // ListStats lists names for all stats.
+ ListStats(patterns ...string) (statNames []string, err error)
+
+ // DumpStats dumps all stat entries.
+ DumpStats(patterns ...string) ([]*StatEntry, error)
+}
+
+// StatType represents type of stat directory and simply
+// defines what type of stat data is stored in the stat entry.
+type StatType int
+
+const (
+ _ StatType = iota
+ ScalarIndex
+ SimpleCounterVector
+ CombinedCounterVector
+ ErrorIndex
+)
+
+func (d StatType) String() string {
+ switch d {
+ case ScalarIndex:
+ return "ScalarIndex"
+ case SimpleCounterVector:
+ return "SimpleCounterVector"
+ case CombinedCounterVector:
+ return "CombinedCounterVector"
+ case ErrorIndex:
+ return "ErrorIndex"
+ }
+ return "UnknownStatType"
+}
+
+// StatEntry represents single stat entry. The type of stat stored in Data
+// is defined by Type.
+type StatEntry struct {
+ Name string
+ Type StatType
+ Data Stat
+}
+
+// Counter represents simple counter with single value.
+type Counter uint64
+
+// CombinedCounter represents counter with two values, for packet count and bytes count.
+type CombinedCounter struct {
+ Packets Counter
+ Bytes Counter
+}
+
+// ScalarStat represents stat for ScalarIndex.
+type ScalarStat float64
+
+// ErrorStat represents stat for ErrorIndex.
+type ErrorStat uint64
+
+// SimpleCounterStat represents stat for SimpleCounterVector.
+// The outer array represents workers and the inner array represents sw_if_index.
+// Values should be aggregated per interface for every worker.
+type SimpleCounterStat [][]Counter
+
+// CombinedCounterStat represents stat for CombinedCounterVector.
+// The outer array represents workers and the inner array represents sw_if_index.
+// Values should be aggregated per interface for every worker.
+type CombinedCounterStat [][]CombinedCounter
+
+// Data represents some type of stat which is usually defined by StatType.
+type Stat interface {
+ // isStat is unexported to limit implementations of Data interface to this package,
+ isStat()
+}
+
+func (ScalarStat) isStat() {}
+func (ErrorStat) isStat() {}
+func (SimpleCounterStat) isStat() {}
+func (CombinedCounterStat) isStat() {}
diff --git a/adapter/adapter.go b/adapter/vpp_api.go
index aa34329..7d14633 100644
--- a/adapter/adapter.go
+++ b/adapter/vpp_api.go
@@ -24,16 +24,17 @@ var ErrNotImplemented = errors.New("not implemented for this OS")
// MsgCallback defines func signature for message callback.
type MsgCallback func(msgID uint16, data []byte)
-// VppAdapter provides connection to VPP. It is responsible for sending and receiving of binary-encoded messages to/from VPP.
-type VppAdapter interface {
+// VppAPI provides connection to VPP binary API.
+// It is responsible for sending and receiving of binary-encoded messages to/from VPP.
+type VppAPI interface {
// Connect connects the process to VPP.
Connect() error
// Disconnect disconnects the process from VPP.
- Disconnect()
+ Disconnect() error
// GetMsgID returns a runtime message ID for the given message name and CRC.
- GetMsgID(msgName string, msgCrc string) (uint16, error)
+ GetMsgID(msgName string, msgCrc string) (msgID uint16, err error)
// SendMsg sends a binary-encoded message to VPP.
SendMsg(context uint32, data []byte) error
diff --git a/adapter/vppapiclient/doc.go b/adapter/vppapiclient/doc.go
new file mode 100644
index 0000000..6505498
--- /dev/null
+++ b/adapter/vppapiclient/doc.go
@@ -0,0 +1,18 @@
+// Copyright (c) 2018 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.
+
+// Package vppapiclient is the default VPP adapter being used for
+// the connection to VPP binary & stats API via shared memory.
+// It is essentially Go wrapper for the VPP vppapiclient library written in C.
+package vppapiclient
diff --git a/adapter/vppapiclient/stat_client.go b/adapter/vppapiclient/stat_client.go
new file mode 100644
index 0000000..5f3b932
--- /dev/null
+++ b/adapter/vppapiclient/stat_client.go
@@ -0,0 +1,279 @@
+// Copyright (c) 2018 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>
+#include <vpp-api/client/stat_client.h>
+
+static int
+govpp_stat_connect(char *socket_name)
+{
+ return stat_segment_connect(socket_name);
+}
+
+static void
+govpp_stat_disconnect()
+{
+ stat_segment_disconnect();
+}
+
+static uint32_t*
+govpp_stat_segment_ls(uint8_t ** pattern)
+{
+ return stat_segment_ls(pattern);
+}
+
+static int
+govpp_stat_segment_vec_len(void *vec)
+{
+ return stat_segment_vec_len(vec);
+}
+
+static void
+govpp_stat_segment_vec_free(void *vec)
+{
+ stat_segment_vec_free(vec);
+}
+
+static char*
+govpp_stat_segment_dir_index_to_name(uint32_t *dir, uint32_t index)
+{
+ return stat_segment_index_to_name(dir[index]);
+}
+
+static stat_segment_data_t*
+govpp_stat_segment_dump(uint32_t *counter_vec)
+{
+ return stat_segment_dump(counter_vec);
+}
+
+static stat_segment_data_t
+govpp_stat_segment_dump_index(stat_segment_data_t *data, int index)
+{
+ return data[index];
+}
+
+static int
+govpp_stat_segment_data_type(stat_segment_data_t *data)
+{
+ return data->type;
+}
+
+static double
+govpp_stat_segment_data_get_scalar_value(stat_segment_data_t *data)
+{
+ return data->scalar_value;
+}
+
+static double
+govpp_stat_segment_data_get_error_value(stat_segment_data_t *data)
+{
+ return data->error_value;
+}
+
+static uint64_t**
+govpp_stat_segment_data_get_simple_counter(stat_segment_data_t *data)
+{
+ return data->simple_counter_vec;
+}
+
+static uint64_t*
+govpp_stat_segment_data_get_simple_counter_index(stat_segment_data_t *data, int index)
+{
+ return data->simple_counter_vec[index];
+}
+
+static uint64_t
+govpp_stat_segment_data_get_simple_counter_index_value(stat_segment_data_t *data, int index, int index2)
+{
+ return data->simple_counter_vec[index][index2];
+}
+
+static vlib_counter_t**
+govpp_stat_segment_data_get_combined_counter(stat_segment_data_t *data)
+{
+ return data->combined_counter_vec;
+}
+
+static vlib_counter_t*
+govpp_stat_segment_data_get_combined_counter_index(stat_segment_data_t *data, int index)
+{
+ return data->combined_counter_vec[index];
+}
+
+static uint64_t
+govpp_stat_segment_data_get_combined_counter_index_packets(stat_segment_data_t *data, int index, int index2)
+{
+ return data->combined_counter_vec[index][index2].packets;
+}
+
+static uint64_t
+govpp_stat_segment_data_get_combined_counter_index_bytes(stat_segment_data_t *data, int index, int index2)
+{
+ return data->combined_counter_vec[index][index2].bytes;
+}
+
+static void
+govpp_stat_segment_data_free(stat_segment_data_t *data)
+{
+ stat_segment_data_free(data);
+}
+
+static uint8_t**
+govpp_stat_segment_string_vector(uint8_t ** string_vector, char *string)
+{
+ return stat_segment_string_vector(string_vector, string);
+}
+*/
+import "C"
+import (
+ "fmt"
+ "os"
+ "unsafe"
+
+ "git.fd.io/govpp.git/adapter"
+)
+
+var (
+ // DefaultStatSocket is the default path for the VPP stat socket file.
+ DefaultStatSocket = "/run/vpp/stats.sock"
+)
+
+// StatClient is the default implementation of StatsAPI.
+type StatClient struct {
+ socketName string
+}
+
+// NewStatClient returns new VPP stats API client.
+func NewStatClient(socketName string) *StatClient {
+ return &StatClient{
+ socketName: socketName,
+ }
+}
+
+func (c *StatClient) Connect() error {
+ var sockName string
+
+ if c.socketName == "" {
+ sockName = DefaultStatSocket
+ } else {
+ sockName = c.socketName
+ }
+
+ rc := C.govpp_stat_connect(C.CString(sockName))
+ if rc != 0 {
+ return fmt.Errorf("connecting to VPP stats API failed (rc=%v)", rc)
+ }
+
+ return nil
+}
+
+func (c *StatClient) Disconnect() error {
+ C.govpp_stat_disconnect()
+ return nil
+}
+
+func (c *StatClient) ListStats(patterns ...string) (stats []string, err error) {
+ dir := C.govpp_stat_segment_ls(convertStringSlice(patterns))
+ defer C.govpp_stat_segment_vec_free(unsafe.Pointer(dir))
+
+ l := C.govpp_stat_segment_vec_len(unsafe.Pointer(dir))
+ for i := 0; i < int(l); i++ {
+ nameChar := C.govpp_stat_segment_dir_index_to_name(dir, C.uint32_t(i))
+ stats = append(stats, C.GoString(nameChar))
+ C.free(unsafe.Pointer(nameChar))
+ }
+
+ return stats, nil
+}
+
+func (c *StatClient) DumpStats(patterns ...string) (stats []*adapter.StatEntry, err error) {
+ dir := C.govpp_stat_segment_ls(convertStringSlice(patterns))
+ defer C.govpp_stat_segment_vec_free(unsafe.Pointer(dir))
+
+ dump := C.govpp_stat_segment_dump(dir)
+ defer C.govpp_stat_segment_data_free(dump)
+
+ l := C.govpp_stat_segment_vec_len(unsafe.Pointer(dump))
+ for i := 0; i < int(l); i++ {
+ v := C.govpp_stat_segment_dump_index(dump, C.int(i))
+ nameChar := v.name
+ name := C.GoString(nameChar)
+ typ := adapter.StatType(C.govpp_stat_segment_data_type(&v))
+
+ stat := &adapter.StatEntry{
+ Name: name,
+ Type: typ,
+ }
+
+ switch typ {
+ case adapter.ScalarIndex:
+ stat.Data = adapter.ScalarStat(C.govpp_stat_segment_data_get_scalar_value(&v))
+
+ case adapter.ErrorIndex:
+ stat.Data = adapter.ErrorStat(C.govpp_stat_segment_data_get_error_value(&v))
+
+ case adapter.SimpleCounterVector:
+ length := int(C.govpp_stat_segment_vec_len(unsafe.Pointer(C.govpp_stat_segment_data_get_simple_counter(&v))))
+ vector := make([][]adapter.Counter, length)
+ for k := 0; k < length; k++ {
+ for j := 0; j < int(C.govpp_stat_segment_vec_len(unsafe.Pointer(C.govpp_stat_segment_data_get_simple_counter_index(&v, _Ctype_int(k))))); j++ {
+ vector[k] = append(vector[k], adapter.Counter(C.govpp_stat_segment_data_get_simple_counter_index_value(&v, _Ctype_int(k), _Ctype_int(j))))
+ }
+ }
+ stat.Data = adapter.SimpleCounterStat(vector)
+
+ case adapter.CombinedCounterVector:
+ length := int(C.govpp_stat_segment_vec_len(unsafe.Pointer(C.govpp_stat_segment_data_get_combined_counter(&v))))
+ vector := make([][]adapter.CombinedCounter, length)
+ for k := 0; k < length; k++ {
+ for j := 0; j < int(C.govpp_stat_segment_vec_len(unsafe.Pointer(C.govpp_stat_segment_data_get_combined_counter_index(&v, _Ctype_int(k))))); j++ {
+ vector[k] = append(vector[k], adapter.CombinedCounter{
+ Packets: adapter.Counter(C.govpp_stat_segment_data_get_combined_counter_index_packets(&v, _Ctype_int(k), _Ctype_int(j))),
+ Bytes: adapter.Counter(C.govpp_stat_segment_data_get_combined_counter_index_bytes(&v, _Ctype_int(k), _Ctype_int(j))),
+ })
+ }
+ }
+ stat.Data = adapter.CombinedCounterStat(vector)
+
+ default:
+ fmt.Fprintf(os.Stderr, "invalid stat type: %v (%d)", typ, typ)
+ continue
+
+ }
+
+ stats = append(stats, stat)
+ }
+
+ return stats, nil
+}
+
+func convertStringSlice(strs []string) **C.uint8_t {
+ var arr **C.uint8_t
+ for _, str := range strs {
+ arr = C.govpp_stat_segment_string_vector(arr, C.CString(str))
+ }
+ return arr
+}
diff --git a/adapter/vppapiclient/stat_client_stub.go b/adapter/vppapiclient/stat_client_stub.go
new file mode 100644
index 0000000..24f7e13
--- /dev/null
+++ b/adapter/vppapiclient/stat_client_stub.go
@@ -0,0 +1,45 @@
+// Copyright (c) 2018 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
+
+import (
+ "git.fd.io/govpp.git/adapter"
+)
+
+// StatClient is just an stub adapter that does nothing. It builds only on Windows and OSX, where the real
+// VPP stats API client adapter does not build. Its sole purpose is to make the compiler happy on Windows and OSX.
+type StatClient struct{}
+
+func NewStatClient(socketName string) *StatClient {
+ return new(StatClient)
+}
+
+func (*StatClient) Connect() error {
+ return adapter.ErrNotImplemented
+}
+
+func (*StatClient) Disconnect() error {
+ return nil
+}
+
+func (*StatClient) ListStats(patterns ...string) (statNames []string, err error) {
+ return nil, adapter.ErrNotImplemented
+}
+
+func (*StatClient) DumpStats(patterns ...string) ([]*adapter.StatEntry, error) {
+ return nil, adapter.ErrNotImplemented
+}
diff --git a/adapter/vppapiclient/vppapiclient_adapter.go b/adapter/vppapiclient/vppapiclient.go
index e62bccd..34ad199 100644
--- a/adapter/vppapiclient/vppapiclient_adapter.go
+++ b/adapter/vppapiclient/vppapiclient.go
@@ -14,8 +14,6 @@
// +build !windows,!darwin
-// Package vppapiclient is the default VPP adapter being used for the connection with VPP via shared memory.
-// It is based on the communication with the vppapiclient VPP library written in C via CGO.
package vppapiclient
/*
@@ -41,7 +39,7 @@ typedef struct __attribute__((__packed__)) _reply_header {
} reply_header_t;
static void
-govpp_msg_callback (unsigned char *data, int size)
+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);
@@ -56,13 +54,13 @@ govpp_send(uint32_t context, void *data, size_t size)
}
static int
-govpp_connect (char *shm)
+govpp_connect(char *shm)
{
return vac_connect("govpp", shm, govpp_msg_callback, 32);
}
static int
-govvp_disconnect()
+govpp_disconnect()
{
return vac_disconnect();
}
@@ -87,32 +85,34 @@ import (
)
const (
- // watchedFolder is a folder where vpp's shared memory is supposed to be created.
- // File system events are monitored in this folder.
- watchedFolder = "/dev/shm/"
- // watchedFile is a default name of the file in the watchedFolder. Once the file is present,
- // the vpp is ready to accept a new connection.
- watchedFile = "vpe-api"
+ // 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"
)
-// vppAPIClientAdapter is the opaque context of the adapter.
-type vppAPIClientAdapter struct {
- shmPrefix string
- callback adapter.MsgCallback
-}
+// global VPP binary API client adapter context
+var client *VppClient
-var vppClient *vppAPIClientAdapter // global vpp API client adapter context
+// VppClient is the default implementation of the VppAPI.
+type VppClient struct {
+ shmPrefix string
+ msgCallback adapter.MsgCallback
+}
-// NewVppAdapter returns a new vpp API client adapter.
-func NewVppAdapter(shmPrefix string) adapter.VppAdapter {
- return &vppAPIClientAdapter{
+// 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 *vppAPIClientAdapter) Connect() error {
- vppClient = a
+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)
@@ -121,18 +121,27 @@ func (a *vppAPIClientAdapter) Connect() error {
rc = C.govpp_connect(shm)
}
if rc != 0 {
- return fmt.Errorf("unable to connect to VPP (error=%d)", rc)
+ return fmt.Errorf("connecting to VPP binary API failed (rc=%v)", rc)
}
+
+ client = a
return nil
}
// Disconnect disconnects the process from VPP.
-func (a *vppAPIClientAdapter) Disconnect() {
- C.govvp_disconnect()
+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 *vppAPIClientAdapter) GetMsgID(msgName string, msgCrc string) (uint16, error) {
+func (a *VppClient) GetMsgID(msgName string, msgCrc string) (uint16, error) {
nameAndCrc := C.CString(msgName + "_" + msgCrc)
defer C.free(unsafe.Pointer(nameAndCrc))
@@ -146,45 +155,56 @@ func (a *vppAPIClientAdapter) GetMsgID(msgName string, msgCrc string) (uint16, e
}
// SendMsg sends a binary-encoded message to VPP.
-func (a *vppAPIClientAdapter) SendMsg(context uint32, data []byte) error {
+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 (error=%d)", rc)
+ 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 *vppAPIClientAdapter) SetMsgCallback(cb adapter.MsgCallback) {
- a.callback = cb
+// 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 *vppAPIClientAdapter) WaitReady() error {
- // Path to the shared memory segment
+func (a *VppClient) WaitReady() error {
var path string
+
+ // join the path to the shared memory segment
if a.shmPrefix == "" {
- path = filepath.Join(watchedFolder, watchedFile)
+ path = filepath.Join(shmDir, vppShmFile)
} else {
- path = filepath.Join(watchedFolder, a.shmPrefix+"-"+watchedFile)
+ path = filepath.Join(shmDir, a.shmPrefix+"-"+vppShmFile)
}
- // Watch folder if file does not exist yet
- if !fileExists(path) {
- watcher, err := fsnotify.NewWatcher()
- if err != nil {
- return err
- }
- defer watcher.Close()
+ // 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
+ }
- if err := watcher.Add(watchedFolder); err != nil {
- 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 && (ev.Op&fsnotify.Create) == fsnotify.Create {
+ for {
+ ev := <-watcher.Events
+ if ev.Name == path {
+ if (ev.Op & fsnotify.Create) == fsnotify.Create {
+ // file was created, we are ready
break
}
}
@@ -193,20 +213,11 @@ func (a *vppAPIClientAdapter) WaitReady() error {
return nil
}
-func fileExists(name string) bool {
- if _, err := os.Stat(name); err != nil {
- if os.IsNotExist(err) {
- return false
- }
- }
- return true
-}
-
//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
- slice := &reflect.SliceHeader{Data: uintptr(data), Len: int(size), Cap: int(size)}
- byteArr := *(*[]byte)(unsafe.Pointer(slice))
+ sliceHeader := &reflect.SliceHeader{Data: uintptr(data), Len: int(size), Cap: int(size)}
+ byteSlice := *(*[]byte)(unsafe.Pointer(sliceHeader))
- vppClient.callback(uint16(msgID), byteArr)
+ client.msgCallback(uint16(msgID), byteSlice)
}
diff --git a/adapter/vppapiclient/empty_adapter.go b/adapter/vppapiclient/vppapiclient_stub.go
index 7514048..6de89a7 100644
--- a/adapter/vppapiclient/empty_adapter.go
+++ b/adapter/vppapiclient/vppapiclient_stub.go
@@ -14,43 +14,40 @@
// +build windows darwin
-/*
- This is just an empty adapter that does nothing. It builds only on Windows and OSX, where the real
- VPP API client adapter does not build. Its sole purpose is to make the compiler happy on Windows and OSX.
-*/
-
package vppapiclient
import (
"git.fd.io/govpp.git/adapter"
)
-type vppAPIClientAdapter struct{}
+// VppClient is just an stub adapter that does nothing. It builds only on Windows and OSX, where the real
+// VPP binary API client adapter does not build. Its sole purpose is to make the compiler happy on Windows and OSX.
+type VppClient struct{}
-func NewVppAdapter(string) adapter.VppAdapter {
- return &vppAPIClientAdapter{}
+func NewVppAdapter(string) *VppClient {
+ return &VppClient{}
}
-func (a *vppAPIClientAdapter) Connect() error {
+func (a *VppClient) Connect() error {
return adapter.ErrNotImplemented
}
-func (a *vppAPIClientAdapter) Disconnect() {
- // no op
+func (a *VppClient) Disconnect() error {
+ return nil
}
-func (a *vppAPIClientAdapter) GetMsgID(msgName string, msgCrc string) (uint16, error) {
+func (a *VppClient) GetMsgID(msgName string, msgCrc string) (uint16, error) {
return 0, nil
}
-func (a *vppAPIClientAdapter) SendMsg(clientID uint32, data []byte) error {
+func (a *VppClient) SendMsg(clientID uint32, data []byte) error {
return nil
}
-func (a *vppAPIClientAdapter) SetMsgCallback(cb adapter.MsgCallback) {
+func (a *VppClient) SetMsgCallback(cb adapter.MsgCallback) {
// no op
}
-func (a *vppAPIClientAdapter) WaitReady() error {
- return nil
+func (a *VppClient) WaitReady() error {
+ return adapter.ErrNotImplemented
}
diff --git a/core/connection.go b/core/connection.go
index 7d014ce..c4048f0 100644
--- a/core/connection.go
+++ b/core/connection.go
@@ -72,8 +72,9 @@ var (
// Connection represents a shared memory connection to VPP via vppAdapter.
type Connection struct {
- vpp adapter.VppAdapter // VPP adapter
- connected uint32 // non-zero if the adapter is connected to VPP
+ vppClient adapter.VppAPI // VPP binary API client adapter
+
+ vppConnected uint32 // non-zero if the adapter is connected to VPP
codec *codec.MsgCodec // message codec
msgIDs map[string]uint16 // map of message IDs indexed by message name + CRC
@@ -93,24 +94,24 @@ type Connection struct {
lastReply time.Time // time of the last received reply from VPP
}
-func newConnection(vpp adapter.VppAdapter) *Connection {
+func newConnection(binapi adapter.VppAPI) *Connection {
c := &Connection{
- vpp: vpp,
+ vppClient: binapi,
codec: &codec.MsgCodec{},
msgIDs: make(map[string]uint16),
msgMap: make(map[uint16]api.Message),
channels: make(map[uint16]*Channel),
subscriptions: make(map[uint16][]*subscriptionCtx),
}
- vpp.SetMsgCallback(c.msgCallback)
+ binapi.SetMsgCallback(c.msgCallback)
return c
}
// Connect connects to VPP using specified VPP adapter and returns the connection handle.
// This call blocks until VPP is connected, or an error occurs. Only one connection attempt will be performed.
-func Connect(vppAdapter adapter.VppAdapter) (*Connection, error) {
+func Connect(binapi adapter.VppAPI) (*Connection, error) {
// create new connection handle
- c, err := createConnection(vppAdapter)
+ c, err := createConnection(binapi)
if err != nil {
return nil, err
}
@@ -127,9 +128,9 @@ func Connect(vppAdapter adapter.VppAdapter) (*Connection, error) {
// and ConnectionState channel. This call does not block until connection is established, it
// returns immediately. The caller is supposed to watch the returned ConnectionState channel for
// Connected/Disconnected events. In case of disconnect, the library will asynchronously try to reconnect.
-func AsyncConnect(vppAdapter adapter.VppAdapter) (*Connection, chan ConnectionEvent, error) {
+func AsyncConnect(binapi adapter.VppAPI) (*Connection, chan ConnectionEvent, error) {
// create new connection handle
- c, err := createConnection(vppAdapter)
+ c, err := createConnection(binapi)
if err != nil {
return nil, nil, err
}
@@ -150,14 +151,14 @@ func (c *Connection) Disconnect() {
connLock.Lock()
defer connLock.Unlock()
- if c.vpp != nil {
+ if c.vppClient != nil {
c.disconnectVPP()
}
conn = nil
}
// newConnection returns new connection handle.
-func createConnection(vppAdapter adapter.VppAdapter) (*Connection, error) {
+func createConnection(binapi adapter.VppAPI) (*Connection, error) {
connLock.Lock()
defer connLock.Unlock()
@@ -165,7 +166,7 @@ func createConnection(vppAdapter adapter.VppAdapter) (*Connection, error) {
return nil, errors.New("only one connection per process is supported")
}
- conn = newConnection(vppAdapter)
+ conn = newConnection(binapi)
return conn, nil
}
@@ -175,19 +176,19 @@ func (c *Connection) connectVPP() error {
log.Debug("Connecting to VPP..")
// blocking connect
- if err := c.vpp.Connect(); err != nil {
+ if err := c.vppClient.Connect(); err != nil {
return err
}
log.Debugf("Connected to VPP.")
if err := c.retrieveMessageIDs(); err != nil {
- c.vpp.Disconnect()
+ c.vppClient.Disconnect()
return fmt.Errorf("VPP is incompatible: %v", err)
}
// store connected state
- atomic.StoreUint32(&c.connected, 1)
+ atomic.StoreUint32(&c.vppConnected, 1)
return nil
}
@@ -272,7 +273,7 @@ func (c *Connection) retrieveMessageIDs() (err error) {
msgs := api.GetAllMessages()
for name, msg := range msgs {
- msgID, err := c.vpp.GetMsgID(msg.GetMessageName(), msg.GetCrcString())
+ msgID, err := c.vppClient.GetMsgID(msg.GetMessageName(), msg.GetCrcString())
if err != nil {
return err
}
@@ -296,14 +297,14 @@ func (c *Connection) retrieveMessageIDs() (err error) {
// fallback for control ping when vpe package is not imported
if c.pingReqID == 0 {
- c.pingReqID, err = c.vpp.GetMsgID(msgControlPing.GetMessageName(), msgControlPing.GetCrcString())
+ c.pingReqID, err = c.vppClient.GetMsgID(msgControlPing.GetMessageName(), msgControlPing.GetCrcString())
if err != nil {
return err
}
addMsg(c.pingReqID, msgControlPing)
}
if c.pingReplyID == 0 {
- c.pingReplyID, err = c.vpp.GetMsgID(msgControlPingReply.GetMessageName(), msgControlPingReply.GetCrcString())
+ c.pingReplyID, err = c.vppClient.GetMsgID(msgControlPingReply.GetMessageName(), msgControlPingReply.GetCrcString())
if err != nil {
return err
}
@@ -315,8 +316,8 @@ func (c *Connection) retrieveMessageIDs() (err error) {
// disconnectVPP disconnects from VPP in case it is connected.
func (c *Connection) disconnectVPP() {
- if atomic.CompareAndSwapUint32(&c.connected, 1, 0) {
- c.vpp.Disconnect()
+ if atomic.CompareAndSwapUint32(&c.vppConnected, 1, 0) {
+ c.vppClient.Disconnect()
}
}
@@ -325,7 +326,7 @@ func (c *Connection) disconnectVPP() {
func (c *Connection) connectLoop(connChan chan ConnectionEvent) {
// loop until connected
for {
- if err := c.vpp.WaitReady(); err != nil {
+ if err := c.vppClient.WaitReady(); err != nil {
log.Warnf("wait ready failed: %v", err)
}
if err := c.connectVPP(); err == nil {
@@ -362,7 +363,7 @@ func (c *Connection) healthCheckLoop(connChan chan ConnectionEvent) {
// sleep until next health check probe period
time.Sleep(HealthCheckProbeInterval)
- if atomic.LoadUint32(&c.connected) == 0 {
+ if atomic.LoadUint32(&c.vppConnected) == 0 {
// Disconnect has been called in the meantime, return the healthcheck - reconnect loop
log.Debug("Disconnected on request, exiting health check loop.")
return
diff --git a/core/request_handler.go b/core/request_handler.go
index e52e262..545f235 100644
--- a/core/request_handler.go
+++ b/core/request_handler.go
@@ -49,7 +49,7 @@ func (c *Connection) watchRequests(ch *Channel) {
// processRequest processes a single request received on the request channel.
func (c *Connection) processRequest(ch *Channel, req *vppRequest) error {
// check whether we are connected to VPP
- if atomic.LoadUint32(&c.connected) == 0 {
+ if atomic.LoadUint32(&c.vppConnected) == 0 {
err := ErrNotConnected
log.Errorf("processing request failed: %v", err)
return err
@@ -95,7 +95,7 @@ func (c *Connection) processRequest(ch *Channel, req *vppRequest) error {
}
// send the request to VPP
- err = c.vpp.SendMsg(context, data)
+ err = c.vppClient.SendMsg(context, data)
if err != nil {
err = fmt.Errorf("unable to send the message: %v", err)
log.WithFields(logger.Fields{
@@ -118,7 +118,7 @@ func (c *Connection) processRequest(ch *Channel, req *vppRequest) error {
"seq_num": req.seqNum,
}).Debug(" -> Sending a control ping to VPP.")
- if err := c.vpp.SendMsg(context, pingData); err != nil {
+ if err := c.vppClient.SendMsg(context, pingData); err != nil {
log.WithFields(logger.Fields{
"context": context,
"msg_id": msgID,
diff --git a/examples/cmd/stats-api/stats_api.go b/examples/cmd/stats-api/stats_api.go
new file mode 100644
index 0000000..74454ab
--- /dev/null
+++ b/examples/cmd/stats-api/stats_api.go
@@ -0,0 +1,66 @@
+// Copyright (c) 2018 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.
+
+package main
+
+import (
+ "fmt"
+ "log"
+
+ "git.fd.io/govpp.git/adapter"
+ "git.fd.io/govpp.git/adapter/vppapiclient"
+)
+
+// This example shows how to work with VPP's new stats API.
+
+func main() {
+ fmt.Println("Starting VPP stats API example..")
+
+ client := vppapiclient.NewStatClient(vppapiclient.DefaultStatSocket)
+
+ // connect to stats API
+ if err := client.Connect(); err != nil {
+ log.Fatalln("connecting client failed:", err)
+ }
+ defer client.Disconnect()
+
+ // list stats by patterns
+ // you can omit parameters to list all stats
+ list, err := client.ListStats("/if", "/sys")
+ if err != nil {
+ log.Fatalln("listing stats failed:", err)
+ }
+
+ for _, stat := range list {
+ fmt.Printf(" - %v\n", stat)
+ }
+ fmt.Printf("listed %d stats\n", len(list))
+
+ // dump stats by patterns to retrieve stats with the stats data
+ stats, err := client.DumpStats()
+ if err != nil {
+ log.Fatalln("dumping stats failed:", err)
+ }
+
+ for _, stat := range stats {
+ switch data := stat.Data.(type) {
+ case adapter.ErrorStat:
+ if data == 0 {
+ // skip printing errors with 0 value
+ continue
+ }
+ }
+ fmt.Printf(" - %-25s %25v %+v\n", stat.Name, stat.Type, stat.Data)
+ }
+}
diff --git a/examples/cmd/stats-client/stats_client.go b/examples/cmd/stats-client/stats_client.go
index f61f975..7a2c313 100644
--- a/examples/cmd/stats-client/stats_client.go
+++ b/examples/cmd/stats-client/stats_client.go
@@ -28,6 +28,15 @@ import (
"git.fd.io/govpp.git/examples/bin_api/stats"
)
+/*
+
+ IMPORTANT NOTICE!
+
+ The binary API module stats used in this example will be deprecated in VPP 19.01.
+ VPP's new stats API should be used, you can find basic usage of new stats API in example stats-api.
+
+*/
+
func main() {
fmt.Println("Starting stats VPP client..")
diff --git a/govpp.go b/govpp.go
index ff45b78..f679242 100644
--- a/govpp.go
+++ b/govpp.go
@@ -20,16 +20,28 @@ import (
"git.fd.io/govpp.git/core"
)
-var vppAdapter adapter.VppAdapter // VPP Adapter that will be used in the subsequent Connect calls
+var (
+ // VPP binary API adapter that will be used in the subsequent Connect calls
+ vppAdapter adapter.VppAPI
+)
+
+func getVppAdapter(shm string) adapter.VppAPI {
+ if vppAdapter == nil {
+ vppAdapter = vppapiclient.NewVppClient(shm)
+ }
+ return vppAdapter
+}
+
+// SetVppAdapter sets the adapter that will be used for connections to VPP in the subsequent `Connect` calls.
+func SetVppAdapter(a adapter.VppAPI) {
+ vppAdapter = a
+}
// Connect connects the govpp core to VPP either using the default VPP Adapter, or using the adapter previously
// set by SetAdapter (useful mostly just for unit/integration tests with mocked VPP adapter).
// This call blocks until VPP is connected, or an error occurs. Only one connection attempt will be performed.
func Connect(shm string) (*core.Connection, error) {
- if vppAdapter == nil {
- vppAdapter = vppapiclient.NewVppAdapter(shm)
- }
- return core.Connect(vppAdapter)
+ return core.Connect(getVppAdapter(shm))
}
// AsyncConnect asynchronously connects the govpp core to VPP either using the default VPP Adapter,
@@ -38,13 +50,5 @@ func Connect(shm string) (*core.Connection, error) {
// supposed to watch the returned ConnectionState channel for Connected/Disconnected events.
// In case of disconnect, the library will asynchronously try to reconnect.
func AsyncConnect(shm string) (*core.Connection, chan core.ConnectionEvent, error) {
- if vppAdapter == nil {
- vppAdapter = vppapiclient.NewVppAdapter(shm)
- }
- return core.AsyncConnect(vppAdapter)
+ return core.AsyncConnect(getVppAdapter(shm))
}
-
-// SetAdapter sets the adapter that will be used for connections to VPP in the subsequent `Connect` calls.
-func SetAdapter(ad adapter.VppAdapter) {
- vppAdapter = ad
-} \ No newline at end of file