9 "git.fd.io/govpp.git/adapter"
10 "git.fd.io/govpp.git/api"
14 SystemStatsPrefix = "/sys/"
15 SystemStats_VectorRate = SystemStatsPrefix + "vector_rate"
16 SystemStats_InputRate = SystemStatsPrefix + "input_rate"
17 SystemStats_LastUpdate = SystemStatsPrefix + "last_update"
18 SystemStats_LastStatsClear = SystemStatsPrefix + "last_stats_clear"
19 SystemStats_Heartbeat = SystemStatsPrefix + "heartbeat"
21 NodeStatsPrefix = "/sys/node/"
22 NodeStats_Names = NodeStatsPrefix + "names"
23 NodeStats_Clocks = NodeStatsPrefix + "clocks"
24 NodeStats_Vectors = NodeStatsPrefix + "vectors"
25 NodeStats_Calls = NodeStatsPrefix + "calls"
26 NodeStats_Suspends = NodeStatsPrefix + "suspends"
28 BufferStatsPrefix = "/buffer-pools/"
29 BufferStats_Cached = "cached"
30 BufferStats_Used = "used"
31 BufferStats_Available = "available"
33 CounterStatsPrefix = "/err/"
35 InterfaceStatsPrefix = "/if/"
36 InterfaceStats_Names = InterfaceStatsPrefix + "names"
37 InterfaceStats_Drops = InterfaceStatsPrefix + "drops"
38 InterfaceStats_Punt = InterfaceStatsPrefix + "punt"
39 InterfaceStats_IP4 = InterfaceStatsPrefix + "ip4"
40 InterfaceStats_IP6 = InterfaceStatsPrefix + "ip6"
41 InterfaceStats_RxNoBuf = InterfaceStatsPrefix + "rx-no-buf"
42 InterfaceStats_RxMiss = InterfaceStatsPrefix + "rx-miss"
43 InterfaceStats_RxError = InterfaceStatsPrefix + "rx-error"
44 InterfaceStats_TxError = InterfaceStatsPrefix + "tx-error"
45 InterfaceStats_Rx = InterfaceStatsPrefix + "rx"
46 InterfaceStats_RxUnicast = InterfaceStatsPrefix + "rx-unicast"
47 InterfaceStats_RxMulticast = InterfaceStatsPrefix + "rx-multicast"
48 InterfaceStats_RxBroadcast = InterfaceStatsPrefix + "rx-broadcast"
49 InterfaceStats_Tx = InterfaceStatsPrefix + "tx"
50 InterfaceStats_TxUnicastMiss = InterfaceStatsPrefix + "tx-unicast-miss"
51 InterfaceStats_TxMulticast = InterfaceStatsPrefix + "tx-multicast"
52 InterfaceStats_TxBroadcast = InterfaceStatsPrefix + "tx-broadcast"
54 // TODO: network stats
55 NetworkStatsPrefix = "/net/"
56 NetworkStats_RouteTo = NetworkStatsPrefix + "route/to"
57 NetworkStats_RouteVia = NetworkStatsPrefix + "route/via"
58 NetworkStats_MRoute = NetworkStatsPrefix + "mroute"
59 NetworkStats_Adjacency = NetworkStatsPrefix + "adjacency"
60 NetworkStats_Punt = NetworkStatsPrefix + "punt"
63 type StatsConnection struct {
64 statsClient adapter.StatsAPI
66 connected uint32 // non-zero if the adapter is connected to VPP
69 func newStatsConnection(stats adapter.StatsAPI) *StatsConnection {
70 return &StatsConnection{
75 // Connect connects to Stats API using specified adapter and returns a connection handle.
76 // This call blocks until it is either connected, or an error occurs.
77 // Only one connection attempt will be performed.
78 func ConnectStats(stats adapter.StatsAPI) (*StatsConnection, error) {
79 c := newStatsConnection(stats)
81 if err := c.connectClient(); err != nil {
88 func (c *StatsConnection) connectClient() error {
89 log.Debug("Connecting to stats..")
91 if err := c.statsClient.Connect(); err != nil {
95 log.Debugf("Connected to stats.")
97 // store connected state
98 atomic.StoreUint32(&c.connected, 1)
103 // Disconnect disconnects from Stats API and releases all connection-related resources.
104 func (c *StatsConnection) Disconnect() {
109 if c.statsClient != nil {
114 func (c *StatsConnection) disconnectClient() {
115 if atomic.CompareAndSwapUint32(&c.connected, 1, 0) {
116 c.statsClient.Disconnect()
120 // GetSystemStats retrieves VPP system stats.
121 func (c *StatsConnection) GetSystemStats() (*api.SystemStats, error) {
122 stats, err := c.statsClient.DumpStats(SystemStatsPrefix)
127 sysStats := &api.SystemStats{}
129 for _, stat := range stats {
131 case SystemStats_VectorRate:
132 sysStats.VectorRate = scalarStatToFloat64(stat.Data)
133 case SystemStats_InputRate:
134 sysStats.InputRate = scalarStatToFloat64(stat.Data)
135 case SystemStats_LastUpdate:
136 sysStats.LastUpdate = scalarStatToFloat64(stat.Data)
137 case SystemStats_LastStatsClear:
138 sysStats.LastStatsClear = scalarStatToFloat64(stat.Data)
139 case SystemStats_Heartbeat:
140 sysStats.Heartbeat = scalarStatToFloat64(stat.Data)
147 // GetErrorStats retrieves VPP error stats.
148 func (c *StatsConnection) GetErrorStats(names ...string) (*api.ErrorStats, error) {
149 var patterns []string
151 patterns = make([]string, len(names))
152 for i, name := range names {
153 patterns[i] = CounterStatsPrefix + name
156 // retrieve all error counters by default
157 patterns = []string{CounterStatsPrefix}
159 stats, err := c.statsClient.DumpStats(patterns...)
164 var errorStats = &api.ErrorStats{}
166 for _, stat := range stats {
167 statName := strings.TrimPrefix(stat.Name, CounterStatsPrefix)
169 /* TODO: deal with stats that contain '/' in node/counter name
170 parts := strings.Split(statName, "/")
171 var nodeName, counterName string
175 counterName = parts[1]
177 nodeName = parts[0] + parts[1]
178 counterName = parts[2]
181 errorStats.Errors = append(errorStats.Errors, api.ErrorCounter{
182 CounterName: statName,
183 Value: errorStatToUint64(stat.Data),
187 return errorStats, nil
190 // GetNodeStats retrieves VPP per node stats.
191 func (c *StatsConnection) GetNodeStats() (*api.NodeStats, error) {
192 stats, err := c.statsClient.DumpStats(NodeStatsPrefix)
197 nodeStats := &api.NodeStats{}
199 var setPerNode = func(perNode []uint64, fn func(c *api.NodeCounters, v uint64)) {
200 if nodeStats.Nodes == nil {
201 nodeStats.Nodes = make([]api.NodeCounters, len(perNode))
202 for i := range perNode {
203 nodeStats.Nodes[i].NodeIndex = uint32(i)
206 for i, v := range perNode {
207 if len(nodeStats.Nodes) <= i {
210 nodeCounters := nodeStats.Nodes[i]
212 nodeStats.Nodes[i] = nodeCounters
216 for _, stat := range stats {
218 case NodeStats_Names:
219 if names, ok := stat.Data.(adapter.NameStat); !ok {
220 return nil, fmt.Errorf("invalid stat type for %s", stat.Name)
222 if nodeStats.Nodes == nil {
223 nodeStats.Nodes = make([]api.NodeCounters, len(names))
224 for i := range names {
225 nodeStats.Nodes[i].NodeIndex = uint32(i)
228 for i, name := range names {
229 nodeStats.Nodes[i].NodeName = string(name)
232 case NodeStats_Clocks:
233 setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) {
236 case NodeStats_Vectors:
237 setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) {
240 case NodeStats_Calls:
241 setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) {
244 case NodeStats_Suspends:
245 setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) {
251 return nodeStats, nil
254 // GetInterfaceStats retrieves VPP per interface stats.
255 func (c *StatsConnection) GetInterfaceStats() (*api.InterfaceStats, error) {
256 stats, err := c.statsClient.DumpStats(InterfaceStatsPrefix)
261 ifStats := &api.InterfaceStats{}
263 var setPerIf = func(perIf []uint64, fn func(c *api.InterfaceCounters, v uint64)) {
264 if ifStats.Interfaces == nil {
265 ifStats.Interfaces = make([]api.InterfaceCounters, len(perIf))
266 for i := range perIf {
267 ifStats.Interfaces[i].InterfaceIndex = uint32(i)
270 for i, v := range perIf {
271 if len(ifStats.Interfaces) <= i {
274 ifCounters := ifStats.Interfaces[i]
276 ifStats.Interfaces[i] = ifCounters
280 for _, stat := range stats {
282 case InterfaceStats_Names:
283 if names, ok := stat.Data.(adapter.NameStat); !ok {
284 return nil, fmt.Errorf("invalid stat type for %s", stat.Name)
286 if ifStats.Interfaces == nil {
287 ifStats.Interfaces = make([]api.InterfaceCounters, len(names))
288 for i := range names {
289 ifStats.Interfaces[i].InterfaceIndex = uint32(i)
292 for i, name := range names {
293 ifStats.Interfaces[i].InterfaceName = string(name)
296 case InterfaceStats_Drops:
297 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
300 case InterfaceStats_Punt:
301 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
304 case InterfaceStats_IP4:
305 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
308 case InterfaceStats_IP6:
309 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
312 case InterfaceStats_RxNoBuf:
313 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
316 case InterfaceStats_RxMiss:
317 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
320 case InterfaceStats_RxError:
321 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
324 case InterfaceStats_TxError:
325 setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) {
328 case InterfaceStats_Rx:
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_RxUnicast:
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_RxMulticast:
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_RxBroadcast:
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_Tx:
361 per := reduceCombinedCounterStat(stat.Data)
362 setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
365 setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
368 case InterfaceStats_TxUnicastMiss:
369 per := reduceCombinedCounterStat(stat.Data)
370 setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
371 c.TxUnicastMiss[0] = v
373 setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
374 c.TxUnicastMiss[1] = v
376 case InterfaceStats_TxMulticast:
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) {
384 case InterfaceStats_TxBroadcast:
385 per := reduceCombinedCounterStat(stat.Data)
386 setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) {
389 setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) {
398 // GetBufferStats retrieves VPP buffer pools stats.
399 func (c *StatsConnection) GetBufferStats() (*api.BufferStats, error) {
400 stats, err := c.statsClient.DumpStats(BufferStatsPrefix)
405 bufStats := &api.BufferStats{
406 Buffer: map[string]api.BufferPool{},
409 for _, stat := range stats {
410 d, f := path.Split(stat.Name)
411 d = strings.TrimSuffix(d, "/")
413 name := strings.TrimPrefix(d, BufferStatsPrefix)
414 b, ok := bufStats.Buffer[name]
420 case BufferStats_Cached:
421 b.Cached = scalarStatToFloat64(stat.Data)
422 case BufferStats_Used:
423 b.Used = scalarStatToFloat64(stat.Data)
424 case BufferStats_Available:
425 b.Available = scalarStatToFloat64(stat.Data)
428 bufStats.Buffer[name] = b
434 func scalarStatToFloat64(stat adapter.Stat) float64 {
435 if s, ok := stat.(adapter.ScalarStat); ok {
441 func errorStatToUint64(stat adapter.Stat) uint64 {
442 if s, ok := stat.(adapter.ErrorStat); ok {
448 func reduceSimpleCounterStat(stat adapter.Stat) []uint64 {
449 if s, ok := stat.(adapter.SimpleCounterStat); ok {
453 var per = make([]uint64, len(s[0]))
454 for _, w := range s {
455 for i, n := range w {
464 func reduceCombinedCounterStat(stat adapter.Stat) [2][]uint64 {
465 if s, ok := stat.(adapter.CombinedCounterStat); ok {
467 return [2][]uint64{{}, {}}
469 var perPackets = make([]uint64, len(s[0]))
470 var perBytes = make([]uint64, len(s[0]))
471 for _, w := range s {
472 for i, n := range w {
473 perPackets[i] += uint64(n.Packets)
474 perBytes[i] += uint64(n.Bytes)
477 return [2][]uint64{perPackets, perBytes}