8 "git.fd.io/govpp.git/adapter"
9 "git.fd.io/govpp.git/api"
13 CounterStatsPrefix = "/err/"
15 SystemStatsPrefix = "/sys/"
16 SystemStats_VectorRate = SystemStatsPrefix + "vector_rate"
17 SystemStats_InputRate = SystemStatsPrefix + "input_rate"
18 SystemStats_LastUpdate = SystemStatsPrefix + "last_update"
19 SystemStats_LastStatsClear = SystemStatsPrefix + "last_stats_clear"
20 SystemStats_Heartbeat = SystemStatsPrefix + "heartbeat"
22 NodeStatsPrefix = "/sys/node/"
23 NodeStats_Names = NodeStatsPrefix + "names"
24 NodeStats_Clocks = NodeStatsPrefix + "clocks"
25 NodeStats_Vectors = NodeStatsPrefix + "vectors"
26 NodeStats_Calls = NodeStatsPrefix + "calls"
27 NodeStats_Suspends = NodeStatsPrefix + "suspends"
29 InterfaceStatsPrefix = "/if/"
30 InterfaceStats_Names = InterfaceStatsPrefix + "names"
31 InterfaceStats_Drops = InterfaceStatsPrefix + "drops"
32 InterfaceStats_Punt = InterfaceStatsPrefix + "punt"
33 InterfaceStats_IP4 = InterfaceStatsPrefix + "ip4"
34 InterfaceStats_IP6 = InterfaceStatsPrefix + "ip6"
35 InterfaceStats_RxNoBuf = InterfaceStatsPrefix + "rx-no-buf"
36 InterfaceStats_RxMiss = InterfaceStatsPrefix + "rx-miss"
37 InterfaceStats_RxError = InterfaceStatsPrefix + "rx-error"
38 InterfaceStats_TxError = InterfaceStatsPrefix + "tx-error"
39 InterfaceStats_Rx = InterfaceStatsPrefix + "rx"
40 InterfaceStats_RxUnicast = InterfaceStatsPrefix + "rx-unicast"
41 InterfaceStats_RxMulticast = InterfaceStatsPrefix + "rx-multicast"
42 InterfaceStats_RxBroadcast = InterfaceStatsPrefix + "rx-broadcast"
43 InterfaceStats_Tx = InterfaceStatsPrefix + "tx"
44 InterfaceStats_TxUnicastMiss = InterfaceStatsPrefix + "tx-unicast-miss"
45 InterfaceStats_TxMulticast = InterfaceStatsPrefix + "tx-multicast"
46 InterfaceStats_TxBroadcast = InterfaceStatsPrefix + "tx-broadcast"
48 NetworkStatsPrefix = "/net/"
49 // TODO: network stats
50 NetworkStats_RouteTo = NetworkStatsPrefix + "route/to"
51 NetworkStats_RouteVia = NetworkStatsPrefix + "route/via"
52 NetworkStats_MRoute = NetworkStatsPrefix + "mroute"
53 NetworkStats_Adjacency = NetworkStatsPrefix + "adjacency"
56 type StatsConnection struct {
57 statsClient adapter.StatsAPI
59 connected uint32 // non-zero if the adapter is connected to VPP
62 func newStatsConnection(stats adapter.StatsAPI) *StatsConnection {
63 return &StatsConnection{
68 // Connect connects to Stats API using specified adapter and returns a connection handle.
69 // This call blocks until it is either connected, or an error occurs.
70 // Only one connection attempt will be performed.
71 func ConnectStats(stats adapter.StatsAPI) (*StatsConnection, error) {
72 c := newStatsConnection(stats)
74 if err := c.connectClient(); err != nil {
81 func (c *StatsConnection) connectClient() error {
82 log.Debug("Connecting to stats..")
84 if err := c.statsClient.Connect(); err != nil {
88 log.Debugf("Connected to stats.")
90 // store connected state
91 atomic.StoreUint32(&c.connected, 1)
96 // Disconnect disconnects from Stats API and releases all connection-related resources.
97 func (c *StatsConnection) Disconnect() {
102 if c.statsClient != nil {
107 func (c *StatsConnection) disconnectClient() {
108 if atomic.CompareAndSwapUint32(&c.connected, 1, 0) {
109 c.statsClient.Disconnect()
113 // GetSystemStats retrieves VPP system stats.
114 func (c *StatsConnection) GetSystemStats() (*api.SystemStats, error) {
115 stats, err := c.statsClient.DumpStats(SystemStatsPrefix)
120 sysStats := &api.SystemStats{}
122 for _, stat := range stats {
124 case SystemStats_VectorRate:
125 sysStats.VectorRate = scalarStatToFloat64(stat.Data)
126 case SystemStats_InputRate:
127 sysStats.InputRate = scalarStatToFloat64(stat.Data)
128 case SystemStats_LastUpdate:
129 sysStats.LastUpdate = scalarStatToFloat64(stat.Data)
130 case SystemStats_LastStatsClear:
131 sysStats.LastStatsClear = scalarStatToFloat64(stat.Data)
132 case SystemStats_Heartbeat:
133 sysStats.Heartbeat = scalarStatToFloat64(stat.Data)
140 // GetErrorStats retrieves VPP error stats.
141 func (c *StatsConnection) GetErrorStats(names ...string) (*api.ErrorStats, error) {
142 var patterns []string
144 patterns = make([]string, len(names))
145 for i, name := range names {
146 patterns[i] = CounterStatsPrefix + name
149 // retrieve all error counters by default
150 patterns = []string{CounterStatsPrefix}
152 stats, err := c.statsClient.DumpStats(patterns...)
157 var errorStats = &api.ErrorStats{}
159 for _, stat := range stats {
160 statName := strings.TrimPrefix(stat.Name, CounterStatsPrefix)
162 /* TODO: deal with stats that contain '/' in node/counter name
163 parts := strings.Split(statName, "/")
164 var nodeName, counterName string
168 counterName = parts[1]
170 nodeName = parts[0] + parts[1]
171 counterName = parts[2]
174 errorStats.Errors = append(errorStats.Errors, api.ErrorCounter{
175 CounterName: statName,
176 Value: errorStatToUint64(stat.Data),
180 return errorStats, nil
183 // GetNodeStats retrieves VPP per node stats.
184 func (c *StatsConnection) GetNodeStats() (*api.NodeStats, error) {
185 stats, err := c.statsClient.DumpStats(NodeStatsPrefix)
190 nodeStats := &api.NodeStats{}
191 var setPerNode = func(perNode []uint64, fn func(c *api.NodeCounters, v uint64)) {
192 if nodeStats.Nodes == nil {
193 nodeStats.Nodes = make([]api.NodeCounters, len(perNode))
194 for i := range perNode {
195 nodeStats.Nodes[i].NodeIndex = uint32(i)
198 for i, v := range perNode {
199 if len(nodeStats.Nodes) <= i {
202 nodeCounters := nodeStats.Nodes[i]
204 nodeStats.Nodes[i] = nodeCounters
208 for _, stat := range stats {
210 case NodeStats_Names:
211 if names, ok := stat.Data.(adapter.NameStat); !ok {
212 return nil, fmt.Errorf("invalid stat type for %s", stat.Name)
214 if nodeStats.Nodes == nil {
215 nodeStats.Nodes = make([]api.NodeCounters, len(names))
216 for i := range names {
217 nodeStats.Nodes[i].NodeIndex = uint32(i)
220 for i, name := range names {
221 nodeStats.Nodes[i].NodeName = string(name)
224 case NodeStats_Clocks:
225 setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) {
228 case NodeStats_Vectors:
229 setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) {
232 case NodeStats_Calls:
233 setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) {
236 case NodeStats_Suspends:
237 setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) {
243 return nodeStats, nil
246 // GetInterfaceStats retrieves VPP per interface stats.
247 func (c *StatsConnection) GetInterfaceStats() (*api.InterfaceStats, error) {
248 stats, err := c.statsClient.DumpStats(InterfaceStatsPrefix)
253 ifStats := &api.InterfaceStats{}
255 var setPerIf = func(perIf []uint64, fn func(c *api.InterfaceCounters, v uint64)) {
256 if ifStats.Interfaces == nil {
257 ifStats.Interfaces = make([]api.InterfaceCounters, len(perIf))
258 for i := range perIf {
259 ifStats.Interfaces[i].InterfaceIndex = uint32(i)
262 for i, v := range perIf {
263 if len(ifStats.Interfaces) <= i {
266 ifCounters := ifStats.Interfaces[i]
268 ifStats.Interfaces[i] = ifCounters
272 for _, stat := range stats {
274 case InterfaceStats_Names:
275 if names, ok := stat.Data.(adapter.NameStat); !ok {
276 return nil, fmt.Errorf("invalid stat type for %s", stat.Name)
278 if ifStats.Interfaces == nil {
279 ifStats.Interfaces = make([]api.InterfaceCounters, len(names))
280 for i := range names {
281 ifStats.Interfaces[i].InterfaceIndex = uint32(i)
284 for i, name := range names {
285 ifStats.Interfaces[i].InterfaceName = string(name)
288 case InterfaceStats_Drops:
289 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
292 case InterfaceStats_Punt:
293 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
296 case InterfaceStats_IP4:
297 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
300 case InterfaceStats_IP6:
301 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
304 case InterfaceStats_RxNoBuf:
305 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
308 case InterfaceStats_RxMiss:
309 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
312 case InterfaceStats_RxError:
313 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
316 case InterfaceStats_TxError:
317 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
320 case InterfaceStats_Rx:
321 per := reduceCombinedCounterStat(stat.Data)
322 setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
325 setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
328 case InterfaceStats_RxUnicast:
329 per := reduceCombinedCounterStat(stat.Data)
330 setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
333 setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
336 case InterfaceStats_RxMulticast:
337 per := reduceCombinedCounterStat(stat.Data)
338 setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
341 setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
344 case InterfaceStats_RxBroadcast:
345 per := reduceCombinedCounterStat(stat.Data)
346 setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
349 setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
352 case InterfaceStats_Tx:
353 per := reduceCombinedCounterStat(stat.Data)
354 setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
357 setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
360 case InterfaceStats_TxUnicastMiss:
361 per := reduceCombinedCounterStat(stat.Data)
362 setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
363 c.TxUnicastMiss[0] = v
365 setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
366 c.TxUnicastMiss[1] = v
368 case InterfaceStats_TxMulticast:
369 per := reduceCombinedCounterStat(stat.Data)
370 setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
373 setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
376 case InterfaceStats_TxBroadcast:
377 per := reduceCombinedCounterStat(stat.Data)
378 setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
381 setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
390 func scalarStatToFloat64(stat adapter.Stat) float64 {
391 if s, ok := stat.(adapter.ScalarStat); ok {
397 func errorStatToUint64(stat adapter.Stat) uint64 {
398 if s, ok := stat.(adapter.ErrorStat); ok {
404 func reduceSimpleCounterStat(stat adapter.Stat) []uint64 {
405 if s, ok := stat.(adapter.SimpleCounterStat); ok {
409 var per = make([]uint64, len(s[0]))
410 for _, w := range s {
411 for i, n := range w {
420 func reduceCombinedCounterStat(stat adapter.Stat) [2][]uint64 {
421 if s, ok := stat.(adapter.CombinedCounterStat); ok {
423 return [2][]uint64{{}, {}}
425 var perPackets = make([]uint64, len(s[0]))
426 var perBytes = make([]uint64, len(s[0]))
427 for _, w := range s {
428 for i, n := range w {
429 perPackets[i] += uint64(n.Packets)
430 perBytes[i] += uint64(n.Bytes)
433 return [2][]uint64{perPackets, perBytes}