diff options
author | Ondrej Fabry <ofabry@cisco.com> | 2018-10-22 14:45:21 +0200 |
---|---|---|
committer | Ondrej Fabry <ofabry@cisco.com> | 2018-10-22 14:56:09 +0200 |
commit | 28df7c855784e784fb0e614c1cca0e565a7ef75f (patch) | |
tree | 7d2eb1b3522d57680df8a6f576356f9dd4dc706c /adapter/vppapiclient | |
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')
-rw-r--r-- | adapter/vppapiclient/doc.go | 18 | ||||
-rw-r--r-- | adapter/vppapiclient/stat_client.go | 279 | ||||
-rw-r--r-- | adapter/vppapiclient/stat_client_stub.go | 45 | ||||
-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 |
5 files changed, 426 insertions, 76 deletions
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 } |