summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOndrej Fabry <ofabry@cisco.com>2019-02-19 13:57:12 +0100
committerOndrej Fabry <ofabry@cisco.com>2019-02-19 14:02:45 +0100
commit45e38494c1d65ad9178ad15f4048c0ab16f98b77 (patch)
treedfbc70131af8faff5fde24fc1bbca8a72a91c102
parentdf05a70f90a1486a86a4156b1b0d68c94f2098b4 (diff)
Introduce higer-level API for retrieving statistics
- see stats-api example Change-Id: I11d29d32b60d25238e75cb6b86ee34842348ab38 Signed-off-by: Ondrej Fabry <ofabry@cisco.com>
-rw-r--r--adapter/stats_api.go24
-rw-r--r--api/binapi.go (renamed from api/api.go)0
-rw-r--r--api/stats.go79
-rw-r--r--core/connection.go25
-rw-r--r--core/request_handler.go8
-rw-r--r--core/stats.go397
-rw-r--r--examples/stats-api/README.md93
-rw-r--r--examples/stats-api/stats_api.go55
8 files changed, 649 insertions, 32 deletions
diff --git a/adapter/stats_api.go b/adapter/stats_api.go
index 4a3f130..3538176 100644
--- a/adapter/stats_api.go
+++ b/adapter/stats_api.go
@@ -14,6 +14,10 @@
package adapter
+import (
+ "fmt"
+)
+
// StatsAPI provides connection to VPP stats API.
type StatsAPI interface {
// Connect establishes client connection to the stats API.
@@ -34,11 +38,11 @@ type StatsAPI interface {
type StatType int
const (
- _ StatType = iota
- ScalarIndex
- SimpleCounterVector
- CombinedCounterVector
- ErrorIndex
+ _ StatType = 0
+ ScalarIndex = 1
+ SimpleCounterVector = 2
+ CombinedCounterVector = 3
+ ErrorIndex = 4
)
func (d StatType) String() string {
@@ -52,7 +56,7 @@ func (d StatType) String() string {
case ErrorIndex:
return "ErrorIndex"
}
- return "UnknownStatType"
+ return fmt.Sprintf("UnknownStatType(%d)", d)
}
// StatEntry represents single stat entry. The type of stat stored in Data
@@ -79,13 +83,13 @@ type ScalarStat float64
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.
+// The outer array represents workers and the inner array represents interface/node/.. indexes.
+// Values should be aggregated per interface/node 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.
+// The outer array represents workers and the inner array represents interface/node/.. indexes.
+// Values should be aggregated per interface/node for every worker.
type CombinedCounterStat [][]CombinedCounter
// Data represents some type of stat which is usually defined by StatType.
diff --git a/api/api.go b/api/binapi.go
index ac9f8a4..ac9f8a4 100644
--- a/api/api.go
+++ b/api/binapi.go
diff --git a/api/stats.go b/api/stats.go
new file mode 100644
index 0000000..ec623c7
--- /dev/null
+++ b/api/stats.go
@@ -0,0 +1,79 @@
+package api
+
+// SystemStats represents global system statistics.
+type SystemStats struct {
+ VectorRate float64
+ InputRate float64
+ LastUpdate float64
+ LastStatsClear float64
+ Heartbeat float64
+}
+
+// NodeStats represents per node statistics.
+type NodeStats struct {
+ Nodes []NodeCounters
+}
+
+// NodeCounters represents node counters.
+type NodeCounters struct {
+ NodeIndex uint32
+ // TODO: node name is not currently retrievable via stats API (will be most likely added in 19.04)
+ //NodeName string
+
+ Clocks uint64
+ Vectors uint64
+ Calls uint64
+ Suspends uint64
+}
+
+// InterfaceStats represents per interface statistics.
+type InterfaceStats struct {
+ Interfaces []InterfaceCounters
+}
+
+// InterfaceCounters represents interface counters.
+type InterfaceCounters struct {
+ InterfaceIndex uint32
+ // TODO: interface name is not currently retrievable via stats API (will be most likely added in 19.04)
+ //InterfaceName string
+
+ RxPackets uint64
+ RxBytes uint64
+ RxErrors uint64
+ TxPackets uint64
+ TxBytes uint64
+ TxErrors uint64
+
+ RxUnicast [2]uint64 // packets[0], bytes[1]
+ RxMulticast [2]uint64 // packets[0], bytes[1]
+ RxBroadcast [2]uint64 // packets[0], bytes[1]
+ TxUnicastMiss [2]uint64 // packets[0], bytes[1]
+ TxMulticast [2]uint64 // packets[0], bytes[1]
+ TxBroadcast [2]uint64 // packets[0], bytes[1]
+
+ Drops uint64
+ Punts uint64
+ IP4 uint64
+ IP6 uint64
+ RxNoBuf uint64
+ RxMiss uint64
+}
+
+// ErrorStats represents statistics per error counter.
+type ErrorStats struct {
+ Errors []ErrorCounter
+}
+
+// ErrorCounter represents error counter.
+type ErrorCounter struct {
+ CounterName string
+ Value uint64
+}
+
+// StatsProvider provides the methods for getting statistics.
+type StatsProvider interface {
+ GetSystemStats() (*SystemStats, error)
+ GetNodeStats() (*NodeStats, error)
+ GetInterfaceStats() (*InterfaceStats, error)
+ GetErrorStats(names ...string) (*ErrorStats, error)
+}
diff --git a/core/connection.go b/core/connection.go
index 14b0af4..a21cc28 100644
--- a/core/connection.go
+++ b/core/connection.go
@@ -41,7 +41,7 @@ var (
HealthCheckThreshold = 1 // number of failed health checks until the error is reported
DefaultReplyTimeout = time.Second * 1 // default timeout for replies from VPP
ReconnectInterval = time.Second * 1 // default interval for reconnect attempts
- MaxReconnectAttempts = 10 // maximum number of reconnect attempts
+ MaxReconnectAttempts = 3 // maximum number of reconnect attempts
)
// ConnectionState represents the current state of the connection to VPP.
@@ -58,6 +58,19 @@ const (
Failed
)
+func (s ConnectionState) String() string {
+ switch s {
+ case Connected:
+ return "Connected"
+ case Disconnected:
+ return "Disconnected"
+ case Failed:
+ return "Failed"
+ default:
+ return fmt.Sprintf("UnknownState(%d)", s)
+ }
+}
+
// ConnectionEvent is a notification about change in the VPP connection state.
type ConnectionEvent struct {
// Timestamp holds the time when the event has been created.
@@ -72,7 +85,8 @@ type ConnectionEvent struct {
// Connection represents a shared memory connection to VPP via vppAdapter.
type Connection struct {
- vppClient adapter.VppAPI // VPP binary API client adapter
+ vppClient adapter.VppAPI // VPP binary API client
+ //statsClient adapter.StatsAPI // VPP stats API client
vppConnected uint32 // non-zero if the adapter is connected to VPP
@@ -107,8 +121,9 @@ func newConnection(binapi adapter.VppAPI) *Connection {
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.
+// Connect connects to VPP API using specified adapter and returns a connection handle.
+// This call blocks until it is either connected, or an error occurs.
+// Only one connection attempt will be performed.
func Connect(binapi adapter.VppAPI) (*Connection, error) {
// create new connection handle
c := newConnection(binapi)
@@ -158,7 +173,7 @@ func (c *Connection) connectVPP() error {
return nil
}
-// Disconnect disconnects from VPP and releases all connection-related resources.
+// Disconnect disconnects from VPP API and releases all connection-related resources.
func (c *Connection) Disconnect() {
if c == nil {
return
diff --git a/core/request_handler.go b/core/request_handler.go
index dc90747..55a825a 100644
--- a/core/request_handler.go
+++ b/core/request_handler.go
@@ -88,11 +88,10 @@ func (c *Connection) processRequest(ch *Channel, req *vppRequest) error {
"context": context,
"is_multi": req.multi,
"msg_id": msgID,
- "msg_name": req.msg.GetMessageName(),
"msg_size": len(data),
"seq_num": req.seqNum,
"msg_crc": req.msg.GetCrcString(),
- }).Debug(" -> Sending a message to VPP.")
+ }).Debugf(" --> sending msg: %s", req.msg.GetMessageName())
}
// send the request to VPP
@@ -117,7 +116,7 @@ func (c *Connection) processRequest(ch *Channel, req *vppRequest) error {
"msg_id": c.pingReqID,
"msg_size": len(pingData),
"seq_num": req.seqNum,
- }).Debug(" -> Sending a control ping to VPP.")
+ }).Debug(" -> sending control ping")
if err := c.vppClient.SendMsg(context, pingData); err != nil {
log.WithFields(logger.Fields{
@@ -159,13 +158,12 @@ func (c *Connection) msgCallback(msgID uint16, data []byte) {
log.WithFields(logger.Fields{
"context": context,
"msg_id": msgID,
- "msg_name": msg.GetMessageName(),
"msg_size": len(data),
"channel": chanID,
"is_multi": isMulti,
"seq_num": seqNum,
"msg_crc": msg.GetCrcString(),
- }).Debug(" <- Received a message from VPP.")
+ }).Debugf(" <- received msg: %s", msg.GetMessageName())
}
if context == 0 || c.isNotificationMessage(msgID) {
diff --git a/core/stats.go b/core/stats.go
new file mode 100644
index 0000000..26b9bc9
--- /dev/null
+++ b/core/stats.go
@@ -0,0 +1,397 @@
+package core
+
+import (
+ "strings"
+ "sync/atomic"
+
+ "git.fd.io/govpp.git/adapter"
+ "git.fd.io/govpp.git/api"
+)
+
+const (
+ CounterStatsPrefix = "/err/"
+
+ SystemStatsPrefix = "/sys/"
+ SystemStats_VectorRate = SystemStatsPrefix + "vector_rate"
+ SystemStats_InputRate = SystemStatsPrefix + "input_rate"
+ SystemStats_LastUpdate = SystemStatsPrefix + "last_update"
+ SystemStats_LastStatsClear = SystemStatsPrefix + "last_stats_clear"
+ SystemStats_Heartbeat = SystemStatsPrefix + "heartbeat"
+
+ NodeStatsPrefix = "/sys/node/"
+ NodeStats_Clocks = NodeStatsPrefix + "clocks"
+ NodeStats_Vectors = NodeStatsPrefix + "vectors"
+ NodeStats_Calls = NodeStatsPrefix + "calls"
+ NodeStats_Suspends = NodeStatsPrefix + "suspends"
+
+ InterfaceStatsPrefix = "/if/"
+ InterfaceStats_Drops = InterfaceStatsPrefix + "drops"
+ InterfaceStats_Punt = InterfaceStatsPrefix + "punt"
+ InterfaceStats_IP4 = InterfaceStatsPrefix + "ip4"
+ InterfaceStats_IP6 = InterfaceStatsPrefix + "ip6"
+ InterfaceStats_RxNoBuf = InterfaceStatsPrefix + "rx-no-buf"
+ InterfaceStats_RxMiss = InterfaceStatsPrefix + "rx-miss"
+ InterfaceStats_RxError = InterfaceStatsPrefix + "rx-error"
+ InterfaceStats_TxError = InterfaceStatsPrefix + "tx-error"
+ InterfaceStats_Rx = InterfaceStatsPrefix + "rx"
+ InterfaceStats_RxUnicast = InterfaceStatsPrefix + "rx-unicast"
+ InterfaceStats_RxMulticast = InterfaceStatsPrefix + "rx-multicast"
+ InterfaceStats_RxBroadcast = InterfaceStatsPrefix + "rx-broadcast"
+ InterfaceStats_Tx = InterfaceStatsPrefix + "tx"
+ InterfaceStats_TxUnicastMiss = InterfaceStatsPrefix + "tx-unicast-miss"
+ InterfaceStats_TxMulticast = InterfaceStatsPrefix + "tx-multicast"
+ InterfaceStats_TxBroadcast = InterfaceStatsPrefix + "tx-broadcast"
+
+ NetworkStatsPrefix = "/net/"
+ NetworkStats_RouteTo = NetworkStatsPrefix + "route/to"
+ NetworkStats_RouteVia = NetworkStatsPrefix + "route/via"
+ NetworkStats_MRoute = NetworkStatsPrefix + "mroute"
+ NetworkStats_Adjacency = NetworkStatsPrefix + "adjacency"
+)
+
+type StatsConnection struct {
+ statsClient adapter.StatsAPI
+
+ connected uint32 // non-zero if the adapter is connected to VPP
+}
+
+func newStatsConnection(stats adapter.StatsAPI) *StatsConnection {
+ return &StatsConnection{
+ statsClient: stats,
+ }
+}
+
+// Connect connects to Stats API using specified adapter and returns a connection handle.
+// This call blocks until it is either connected, or an error occurs.
+// Only one connection attempt will be performed.
+func ConnectStats(stats adapter.StatsAPI) (*StatsConnection, error) {
+ c := newStatsConnection(stats)
+
+ if err := c.connectClient(); err != nil {
+ return nil, err
+ }
+
+ return c, nil
+}
+
+func (c *StatsConnection) connectClient() error {
+ log.Debug("Connecting to stats..")
+
+ if err := c.statsClient.Connect(); err != nil {
+ return err
+ }
+
+ log.Debugf("Connected to stats.")
+
+ // store connected state
+ atomic.StoreUint32(&c.connected, 1)
+
+ return nil
+}
+
+// Disconnect disconnects from Stats API and releases all connection-related resources.
+func (c *StatsConnection) Disconnect() {
+ if c == nil {
+ return
+ }
+
+ if c.statsClient != nil {
+ c.disconnectClient()
+ }
+}
+
+func (c *StatsConnection) disconnectClient() {
+ if atomic.CompareAndSwapUint32(&c.connected, 1, 0) {
+ c.statsClient.Disconnect()
+ }
+}
+
+// GetSystemStats retrieves VPP system stats.
+func (c *StatsConnection) GetSystemStats() (*api.SystemStats, error) {
+ stats, err := c.statsClient.DumpStats(SystemStatsPrefix)
+ if err != nil {
+ return nil, err
+ }
+
+ sysStats := &api.SystemStats{}
+
+ for _, stat := range stats {
+ switch stat.Name {
+ case SystemStats_VectorRate:
+ sysStats.VectorRate = scalarStatToFloat64(stat.Data)
+ case SystemStats_InputRate:
+ sysStats.InputRate = scalarStatToFloat64(stat.Data)
+ case SystemStats_LastUpdate:
+ sysStats.LastUpdate = scalarStatToFloat64(stat.Data)
+ case SystemStats_LastStatsClear:
+ sysStats.LastStatsClear = scalarStatToFloat64(stat.Data)
+ case SystemStats_Heartbeat:
+ sysStats.Heartbeat = scalarStatToFloat64(stat.Data)
+ }
+ }
+
+ return sysStats, nil
+}
+
+// GetErrorStats retrieves VPP error stats.
+func (c *StatsConnection) GetErrorStats(names ...string) (*api.ErrorStats, error) {
+ var patterns []string
+ if len(names) > 0 {
+ patterns = make([]string, len(names))
+ for i, name := range names {
+ patterns[i] = CounterStatsPrefix + name
+ }
+ } else {
+ // retrieve all error counters by default
+ patterns = []string{CounterStatsPrefix}
+ }
+ stats, err := c.statsClient.DumpStats(patterns...)
+ if err != nil {
+ return nil, err
+ }
+
+ var errorStats = &api.ErrorStats{}
+
+ for _, stat := range stats {
+ statName := strings.TrimPrefix(stat.Name, CounterStatsPrefix)
+
+ /* TODO: deal with stats that contain '/' in node/counter name
+ parts := strings.Split(statName, "/")
+ var nodeName, counterName string
+ switch len(parts) {
+ case 2:
+ nodeName = parts[0]
+ counterName = parts[1]
+ case 3:
+ nodeName = parts[0] + parts[1]
+ counterName = parts[2]
+ }*/
+
+ errorStats.Errors = append(errorStats.Errors, api.ErrorCounter{
+ CounterName: statName,
+ Value: errorStatToUint64(stat.Data),
+ })
+ }
+
+ return errorStats, nil
+}
+
+// GetNodeStats retrieves VPP per node stats.
+func (c *StatsConnection) GetNodeStats() (*api.NodeStats, error) {
+ stats, err := c.statsClient.DumpStats(NodeStatsPrefix)
+ if err != nil {
+ return nil, err
+ }
+
+ nodeStats := &api.NodeStats{}
+ var setPerNode = func(perNode []uint64, fn func(c *api.NodeCounters, v uint64)) {
+ if nodeStats.Nodes == nil {
+ nodeStats.Nodes = make([]api.NodeCounters, len(perNode))
+ for i := range perNode {
+ nodeStats.Nodes[i].NodeIndex = uint32(i)
+ }
+ }
+ for i, v := range perNode {
+ nodeCounters := nodeStats.Nodes[i]
+ fn(&nodeCounters, v)
+ nodeStats.Nodes[i] = nodeCounters
+ }
+ }
+
+ for _, stat := range stats {
+ switch stat.Name {
+ case NodeStats_Clocks:
+ setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) {
+ c.Clocks = v
+ })
+ case NodeStats_Vectors:
+ setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) {
+ c.Vectors = v
+ })
+ case NodeStats_Calls:
+ setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) {
+ c.Calls = v
+ })
+ case NodeStats_Suspends:
+ setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) {
+ c.Suspends = v
+ })
+ }
+ }
+
+ return nodeStats, nil
+}
+
+// GetInterfaceStats retrieves VPP per interface stats.
+func (c *StatsConnection) GetInterfaceStats() (*api.InterfaceStats, error) {
+ stats, err := c.statsClient.DumpStats(InterfaceStatsPrefix)
+ if err != nil {
+ return nil, err
+ }
+
+ ifStats := &api.InterfaceStats{}
+ var setPerIf = func(perIf []uint64, fn func(c *api.InterfaceCounters, v uint64)) {
+ if ifStats.Interfaces == nil {
+ ifStats.Interfaces = make([]api.InterfaceCounters, len(perIf))
+ for i := range perIf {
+ ifStats.Interfaces[i].InterfaceIndex = uint32(i)
+ }
+ }
+ for i, v := range perIf {
+ ifCounters := ifStats.Interfaces[i]
+ fn(&ifCounters, v)
+ ifStats.Interfaces[i] = ifCounters
+ }
+ }
+
+ for _, stat := range stats {
+ switch stat.Name {
+ case InterfaceStats_Drops:
+ setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
+ c.Drops = v
+ })
+ case InterfaceStats_Punt:
+ setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
+ c.Punts = v
+ })
+ case InterfaceStats_IP4:
+ setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
+ c.IP4 = v
+ })
+ case InterfaceStats_IP6:
+ setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
+ c.IP6 = v
+ })
+ case InterfaceStats_RxNoBuf:
+ setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
+ c.RxNoBuf = v
+ })
+ case InterfaceStats_RxMiss:
+ setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
+ c.RxMiss = v
+ })
+ case InterfaceStats_RxError:
+ setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
+ c.RxErrors = v
+ })
+ case InterfaceStats_TxError:
+ setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
+ c.TxErrors = v
+ })
+ case InterfaceStats_Rx:
+ per := reduceCombinedCounterStat(stat.Data)
+ setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
+ c.RxPackets = v
+ })
+ setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
+ c.RxBytes = v
+ })
+ case InterfaceStats_RxUnicast:
+ per := reduceCombinedCounterStat(stat.Data)
+ setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
+ c.RxUnicast[0] = v
+ })
+ setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
+ c.RxUnicast[1] = v
+ })
+ case InterfaceStats_RxMulticast:
+ per := reduceCombinedCounterStat(stat.Data)
+ setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
+ c.RxMulticast[0] = v
+ })
+ setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
+ c.RxMulticast[1] = v
+ })
+ case InterfaceStats_RxBroadcast:
+ per := reduceCombinedCounterStat(stat.Data)
+ setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
+ c.RxBroadcast[0] = v
+ })
+ setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
+ c.RxBroadcast[1] = v
+ })
+ case InterfaceStats_Tx:
+ per := reduceCombinedCounterStat(stat.Data)
+ setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
+ c.TxPackets = v
+ })
+ setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
+ c.TxBytes = v
+ })
+ case InterfaceStats_TxUnicastMiss:
+ per := reduceCombinedCounterStat(stat.Data)
+ setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
+ c.TxUnicastMiss[0] = v
+ })
+ setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
+ c.TxUnicastMiss[1] = v
+ })
+ case InterfaceStats_TxMulticast:
+ per := reduceCombinedCounterStat(stat.Data)
+ setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
+ c.TxMulticast[0] = v
+ })
+ setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
+ c.TxMulticast[1] = v
+ })
+ case InterfaceStats_TxBroadcast:
+ per := reduceCombinedCounterStat(stat.Data)
+ setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
+ c.TxBroadcast[0] = v
+ })
+ setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
+ c.TxBroadcast[1] = v
+ })
+ }
+ }
+
+ return ifStats, nil
+}
+
+func scalarStatToFloat64(stat adapter.Stat) float64 {
+ if s, ok := stat.(adapter.ScalarStat); ok {
+ return float64(s)
+ }
+ return 0
+}
+
+func errorStatToUint64(stat adapter.Stat) uint64 {
+ if s, ok := stat.(adapter.ErrorStat); ok {
+ return uint64(s)
+ }
+ return 0
+}
+
+func reduceSimpleCounterStat(stat adapter.Stat) []uint64 {
+ if s, ok := stat.(adapter.SimpleCounterStat); ok {
+ if len(s) == 0 {
+ return []uint64{}
+ }
+ var per = make([]uint64, len(s[0]))
+ for _, w := range s {
+ for i, n := range w {
+ per[i] += uint64(n)
+ }
+ }
+ return per
+ }
+ return nil
+}
+
+func reduceCombinedCounterStat(stat adapter.Stat) [2][]uint64 {
+ if s, ok := stat.(adapter.CombinedCounterStat); ok {
+ if len(s) == 0 {
+ return [2][]uint64{{}, {}}
+ }
+ var perPackets = make([]uint64, len(s[0]))
+ var perBytes = make([]uint64, len(s[0]))
+ for _, w := range s {
+ for i, n := range w {
+ perPackets[i] += uint64(n.Packets)
+ perBytes[i] += uint64(n.Bytes)
+ }
+ }
+ return [2][]uint64{perPackets, perBytes}
+ }
+ return [2][]uint64{}
+}
diff --git a/examples/stats-api/README.md b/examples/stats-api/README.md
index 0f26197..77afad8 100644
--- a/examples/stats-api/README.md
+++ b/examples/stats-api/README.md
@@ -12,18 +12,96 @@ The following requirements are required to run this example:
```sh
statseg {
default
+ per-node-counters on
}
- ```
+ ```
> The [default socket](https://wiki.fd.io/view/VPP/Command-line_Arguments#.22statseg.22_parameters) is located at `/run/vpp/stats.sock`.
-- run the VPP, ideally with some traffic
+- run the VPP (ideally with some traffic)
## Running example
-First build the example: `go build git.fd.io/govpp.git/examples/stats-api`.
+First build the example: `go build git.fd.io/govpp.git/examples/stats-api`.
-Use commands `ls` and `dump` to list and dump statistics. Optionally, patterns can be used to filter the results.
+### Higher-level access to stats
-### List stats matching patterns `/sys/` and `/if/`
+Use commands following commands to retrieve stats that are aggregated and
+processed into logical structures from [api package](../../api).
+
+- `system` to retrieve system statistics
+- `nodes` to retrieve per node statistics
+- `interfaces` to retrieve per interface statistics
+- `errors` to retrieve error statistics (you can use patterns to filter the errors)
+
+#### System stats
+
+Following command will retrieve system stats.
+```
+$ ./stats-api system
+System stats: &{VectorRate:0 InputRate:0 LastUpdate:32560 LastStatsClear:0 Heartbeat:3255}
+```
+
+#### Node stats
+
+Following command will retrieve per node stats.
+```
+$ ./stats-api nodes
+Listing node stats..
+...
+ - {NodeIndex:554 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:555 Clocks:189609 Vectors:15 Calls:15 Suspends:0}
+ - {NodeIndex:556 Clocks:2281847 Vectors:0 Calls:0 Suspends:21}
+ - {NodeIndex:557 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:558 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:559 Clocks:7094 Vectors:0 Calls:1 Suspends:1}
+ - {NodeIndex:560 Clocks:88159323916601 Vectors:0 Calls:14066116 Suspends:0}
+ - {NodeIndex:561 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:562 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:563 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:564 Clocks:447894125 Vectors:0 Calls:0 Suspends:32395}
+ - {NodeIndex:565 Clocks:1099655497824612 Vectors:0 Calls:40 Suspends:117}
+ - {NodeIndex:566 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:567 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:568 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:569 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:570 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:571 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:572 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+Listed 573 node counters
+```
+
+#### Interface stats
+
+Following command will retrieve per interface stats.
+```
+$ ./stats-api interfaces
+Listing interface stats..
+ - {InterfaceIndex:0 RxPackets:0 RxBytes:0 RxErrors:0 TxPackets:0 TxBytes:0 TxErrors:0 RxUnicast:[0 0] RxMulticast:[0 0] RxBroadcast:[0 0] TxUnicastMiss:[0 0] TxMulticast:[0 0] TxBroadcast:[0 0] Drops:0 Punts:0 IP4:0 IP6:0 RxNoBuf:0 RxMiss:0}
+ - {InterfaceIndex:1 RxPackets:0 RxBytes:0 RxErrors:0 TxPackets:0 TxBytes:0 TxErrors:0 RxUnicast:[0 0] RxMulticast:[0 0] RxBroadcast:[0 0] TxUnicastMiss:[0 0] TxMulticast:[0 0] TxBroadcast:[0 0] Drops:5 Punts:0 IP4:0 IP6:0 RxNoBuf:0 RxMiss:0}
+ - {InterfaceIndex:2 RxPackets:0 RxBytes:0 RxErrors:0 TxPackets:0 TxBytes:0 TxErrors:0 RxUnicast:[0 0] RxMulticast:[0 0] RxBroadcast:[0 0] TxUnicastMiss:[0 0] TxMulticast:[0 0] TxBroadcast:[0 0] Drops:0 Punts:0 IP4:0 IP6:0 RxNoBuf:0 RxMiss:0}
+ - {InterfaceIndex:3 RxPackets:0 RxBytes:0 RxErrors:0 TxPackets:0 TxBytes:0 TxErrors:0 RxUnicast:[0 0] RxMulticast:[0 0] RxBroadcast:[0 0] TxUnicastMiss:[0 0] TxMulticast:[0 0] TxBroadcast:[0 0] Drops:0 Punts:0 IP4:0 IP6:0 RxNoBuf:0 RxMiss:0}
+Listed 4 interface counters
+```
+
+#### Error stats
+
+Following command will retrieve error stats.
+Use flag `-all` to include stats with zero values.
+```
+$ ./stats-api errors ip
+Listing error stats.. ip
+ - {ip4-input/ip4 spoofed local-address packet drops 15}
+Listed 1 (825) error counters
+```
+
+### Low-level access to stats
+
+Use commands `ls` and `dump` to list and dump statistics in their raw format
+from [adapter package](../../adapter).
+Optionally, patterns can be used to filter the results.
+
+#### List stats
+
+Following command will list stats matching patterns `/sys/` and `/if/`.
```
$ ./stats-api ls /sys/ /if/
Listing stats.. /sys/ /if/
@@ -55,7 +133,10 @@ Listing stats.. /sys/ /if/
Listed 25 stats
```
-### Dump all stats with their types and values
+#### Dump stats
+
+Following command will dump stats with their types and actual values.
+Use flag `-all` to include stats with zero values.
```
$ ./stats-api dump
Dumping stats..
diff --git a/examples/stats-api/stats_api.go b/examples/stats-api/stats_api.go
index 6fd46d2..b905f60 100644
--- a/examples/stats-api/stats_api.go
+++ b/examples/stats-api/stats_api.go
@@ -23,6 +23,7 @@ import (
"git.fd.io/govpp.git/adapter"
"git.fd.io/govpp.git/adapter/vppapiclient"
+ "git.fd.io/govpp.git/core"
)
// ------------------------------------------------------------------
@@ -39,7 +40,7 @@ var (
func init() {
flag.Usage = func() {
- fmt.Fprintf(os.Stderr, "%s: usage [ls|dump] <patterns>...\n", os.Args[0])
+ fmt.Fprintf(os.Stderr, "%s: usage [ls|dump|errors|interfaces|nodes|system] <patterns>...\n", os.Args[0])
flag.PrintDefaults()
os.Exit(1)
}
@@ -47,11 +48,11 @@ func init() {
func main() {
flag.Parse()
+ skipZeros := !*dumpAll
cmd := flag.Arg(0)
-
switch cmd {
- case "", "ls", "dump":
+ case "", "ls", "dump", "errors", "interfaces", "nodes", "system":
default:
flag.Usage()
}
@@ -65,14 +66,56 @@ func main() {
fmt.Printf("Connecting to stats socket: %s\n", *statsSocket)
- if err := client.Connect(); err != nil {
+ c, err := core.ConnectStats(client)
+ if err != nil {
log.Fatalln("Connecting failed:", err)
}
- defer client.Disconnect()
+ defer c.Disconnect()
switch cmd {
+ case "system":
+ stats, err := c.GetSystemStats()
+ if err != nil {
+ log.Fatalln("getting system stats failed:", err)
+ }
+ fmt.Printf("System stats: %+v\n", stats)
+ case "nodes":
+ fmt.Println("Listing node stats..")
+ stats, err := c.GetNodeStats()
+ if err != nil {
+ log.Fatalln("getting node stats failed:", err)
+ }
+ for _, iface := range stats.Nodes {
+ fmt.Printf(" - %+v\n", iface)
+ }
+ fmt.Printf("Listed %d node counters\n", len(stats.Nodes))
+ case "interfaces":
+ fmt.Println("Listing interface stats..")
+ stats, err := c.GetInterfaceStats()
+ if err != nil {
+ log.Fatalln("getting interface stats failed:", err)
+ }
+ for _, iface := range stats.Interfaces {
+ fmt.Printf(" - %+v\n", iface)
+ }
+ fmt.Printf("Listed %d interface counters\n", len(stats.Interfaces))
+ case "errors":
+ fmt.Printf("Listing error stats.. %s\n", strings.Join(patterns, " "))
+ stats, err := c.GetErrorStats(patterns...)
+ if err != nil {
+ log.Fatalln("getting error stats failed:", err)
+ }
+ n := 0
+ for _, counter := range stats.Errors {
+ if counter.Value == 0 && skipZeros {
+ continue
+ }
+ fmt.Printf(" - %v\n", counter)
+ n++
+ }
+ fmt.Printf("Listed %d (%d) error counters\n", n, len(stats.Errors))
case "dump":
- dumpStats(client, patterns, !*dumpAll)
+ dumpStats(client, patterns, skipZeros)
default:
listStats(client, patterns)
}