9 "git.fd.io/govpp.git/adapter"
10 "git.fd.io/govpp.git/api"
15 RetryUpdateDelay = time.Millisecond * 10
19 SystemStatsPrefix = "/sys/"
20 SystemStats_VectorRate = SystemStatsPrefix + "vector_rate"
21 SystemStats_NumWorkerThreads = SystemStatsPrefix + "num_worker_threads"
22 SystemStats_VectorRatePerWorker = SystemStatsPrefix + "vector_rate_per_worker"
23 SystemStats_InputRate = SystemStatsPrefix + "input_rate"
24 SystemStats_LastUpdate = SystemStatsPrefix + "last_update"
25 SystemStats_LastStatsClear = SystemStatsPrefix + "last_stats_clear"
26 SystemStats_Heartbeat = SystemStatsPrefix + "heartbeat"
28 NodeStatsPrefix = "/sys/node/"
29 NodeStats_Names = NodeStatsPrefix + "names"
30 NodeStats_Clocks = NodeStatsPrefix + "clocks"
31 NodeStats_Vectors = NodeStatsPrefix + "vectors"
32 NodeStats_Calls = NodeStatsPrefix + "calls"
33 NodeStats_Suspends = NodeStatsPrefix + "suspends"
35 BufferStatsPrefix = "/buffer-pools/"
36 BufferStats_Cached = "cached"
37 BufferStats_Used = "used"
38 BufferStats_Available = "available"
40 CounterStatsPrefix = "/err/"
42 MemoryStatPrefix = "/mem/statseg"
43 MemoryStats_Total = "total"
44 MemoryStats_Used = "used"
46 InterfaceStatsPrefix = "/if/"
47 InterfaceStats_Names = InterfaceStatsPrefix + "names"
48 InterfaceStats_Drops = InterfaceStatsPrefix + "drops"
49 InterfaceStats_Punt = InterfaceStatsPrefix + "punt"
50 InterfaceStats_IP4 = InterfaceStatsPrefix + "ip4"
51 InterfaceStats_IP6 = InterfaceStatsPrefix + "ip6"
52 InterfaceStats_RxNoBuf = InterfaceStatsPrefix + "rx-no-buf"
53 InterfaceStats_RxMiss = InterfaceStatsPrefix + "rx-miss"
54 InterfaceStats_RxError = InterfaceStatsPrefix + "rx-error"
55 InterfaceStats_TxError = InterfaceStatsPrefix + "tx-error"
56 InterfaceStats_Mpls = InterfaceStatsPrefix + "mpls"
57 InterfaceStats_Rx = InterfaceStatsPrefix + "rx"
58 InterfaceStats_RxUnicast = InterfaceStatsPrefix + "rx-unicast"
59 InterfaceStats_RxMulticast = InterfaceStatsPrefix + "rx-multicast"
60 InterfaceStats_RxBroadcast = InterfaceStatsPrefix + "rx-broadcast"
61 InterfaceStats_Tx = InterfaceStatsPrefix + "tx"
62 InterfaceStats_TxUnicast = InterfaceStatsPrefix + "tx-unicast"
63 InterfaceStats_TxUnicastMiss = InterfaceStatsPrefix + "tx-unicast-miss"
64 InterfaceStats_TxMulticast = InterfaceStatsPrefix + "tx-multicast"
65 InterfaceStats_TxBroadcast = InterfaceStatsPrefix + "tx-broadcast"
67 // TODO: network stats
68 NetworkStatsPrefix = "/net/"
69 NetworkStats_RouteTo = NetworkStatsPrefix + "route/to"
70 NetworkStats_RouteVia = NetworkStatsPrefix + "route/via"
71 NetworkStats_MRoute = NetworkStatsPrefix + "mroute"
72 NetworkStats_Adjacency = NetworkStatsPrefix + "adjacency"
73 NetworkStats_Punt = NetworkStatsPrefix + "punt"
76 type StatsConnection struct {
77 statsClient adapter.StatsAPI
79 // connected is true if the adapter is connected to VPP
82 errorStatsData *adapter.StatDir
83 nodeStatsData *adapter.StatDir
84 ifaceStatsData *adapter.StatDir
85 sysStatsData *adapter.StatDir
86 bufStatsData *adapter.StatDir
87 memStatsData *adapter.StatDir
90 func newStatsConnection(stats adapter.StatsAPI) *StatsConnection {
91 return &StatsConnection{
96 // ConnectStats connects to Stats API using specified adapter and returns a connection handle.
97 // This call blocks until it is either connected, or an error occurs.
98 // Only one connection attempt will be performed.
99 func ConnectStats(stats adapter.StatsAPI) (*StatsConnection, error) {
100 c := newStatsConnection(stats)
102 if err := c.connectClient(); err != nil {
109 func (c *StatsConnection) connectClient() error {
110 log.Debug("Connecting to stats..")
112 if err := c.statsClient.Connect(); err != nil {
116 log.Debugf("Connected to stats.")
118 // store connected state
119 atomic.StoreUint32(&c.connected, 1)
124 // Disconnect disconnects from Stats API and releases all connection-related resources.
125 func (c *StatsConnection) Disconnect() {
129 if c.statsClient != nil {
134 func (c *StatsConnection) disconnectClient() {
135 if atomic.CompareAndSwapUint32(&c.connected, 1, 0) {
136 if err := c.statsClient.Disconnect(); err != nil {
137 log.Debugf("disconnecting stats client failed: %v", err)
142 func (c *StatsConnection) updateStats(statDir **adapter.StatDir, patterns ...string) error {
144 panic("statDir must not nil")
146 try := func() error {
147 if (*statDir) == nil {
148 dir, err := c.statsClient.PrepareDir(patterns...)
150 log.Debugln("preparing dir failed:", err)
155 if err := c.statsClient.UpdateDir(*statDir); err != nil {
156 log.Debugln("updating dir failed:", err)
165 for r := 0; r < RetryUpdateCount; r++ {
166 if err = try(); err == nil {
168 log.Debugf("retry successfull (r=%d)", r)
171 } else if err == adapter.ErrStatsDirStale || err == adapter.ErrStatsDataBusy {
174 log.Debugf("sleeping for %v before next try", RetryUpdateDelay)
175 time.Sleep(RetryUpdateDelay)
178 // error is not retryable
185 // GetSystemStats retrieves VPP system stats.
186 func (c *StatsConnection) GetSystemStats(sysStats *api.SystemStats) (err error) {
187 if err := c.updateStats(&c.sysStatsData, SystemStatsPrefix); err != nil {
191 for _, stat := range c.sysStatsData.Entries {
193 if s, ok := stat.Data.(adapter.ScalarStat); ok {
196 switch string(stat.Name) {
197 case SystemStats_VectorRate:
198 sysStats.VectorRate = val
199 case SystemStats_NumWorkerThreads:
200 sysStats.NumWorkerThreads = val
201 case SystemStats_VectorRatePerWorker:
203 if ss, ok := stat.Data.(adapter.SimpleCounterStat); ok {
204 vals = make([]uint64, len(ss))
209 vals[w] = uint64(ss[w][0])
212 sysStats.VectorRatePerWorker = vals
213 case SystemStats_InputRate:
214 sysStats.InputRate = val
215 case SystemStats_LastUpdate:
216 sysStats.LastUpdate = val
217 case SystemStats_LastStatsClear:
218 sysStats.LastStatsClear = val
219 case SystemStats_Heartbeat:
220 sysStats.Heartbeat = val
227 // GetErrorStats retrieves VPP error stats.
228 func (c *StatsConnection) GetErrorStats(errorStats *api.ErrorStats) (err error) {
229 if err := c.updateStats(&c.errorStatsData, CounterStatsPrefix); err != nil {
233 if errorStats.Errors == nil || len(errorStats.Errors) != len(c.errorStatsData.Entries) {
234 errorStats.Errors = make([]api.ErrorCounter, len(c.errorStatsData.Entries))
235 for i := 0; i < len(c.errorStatsData.Entries); i++ {
236 errorStats.Errors[i].CounterName = string(c.errorStatsData.Entries[i].Name)
240 for i, stat := range c.errorStatsData.Entries {
241 if stat.Type != adapter.ErrorIndex {
244 if errStat, ok := stat.Data.(adapter.ErrorStat); ok {
245 errorStats.Errors[i].Value = uint64(errStat)
252 func (c *StatsConnection) GetNodeStats(nodeStats *api.NodeStats) (err error) {
253 if err := c.updateStats(&c.nodeStatsData, NodeStatsPrefix); err != nil {
257 prepNodes := func(l int) {
258 if nodeStats.Nodes == nil || len(nodeStats.Nodes) != l {
259 nodeStats.Nodes = make([]api.NodeCounters, l)
260 for i := 0; i < l; i++ {
261 nodeStats.Nodes[i].NodeIndex = uint32(i)
265 perNode := func(stat adapter.StatEntry, fn func(*api.NodeCounters, uint64)) {
266 if s, ok := stat.Data.(adapter.SimpleCounterStat); ok {
268 for i := range nodeStats.Nodes {
269 val := adapter.ReduceSimpleCounterStatIndex(s, i)
270 fn(&nodeStats.Nodes[i], val)
275 for _, stat := range c.nodeStatsData.Entries {
276 switch string(stat.Name) {
277 case NodeStats_Names:
278 if stat, ok := stat.Data.(adapter.NameStat); ok {
280 for i, nc := range nodeStats.Nodes {
281 if nc.NodeName != string(stat[i]) {
282 nc.NodeName = string(stat[i])
283 nodeStats.Nodes[i] = nc
287 case NodeStats_Clocks:
288 perNode(stat, func(node *api.NodeCounters, val uint64) {
291 case NodeStats_Vectors:
292 perNode(stat, func(node *api.NodeCounters, val uint64) {
295 case NodeStats_Calls:
296 perNode(stat, func(node *api.NodeCounters, val uint64) {
299 case NodeStats_Suspends:
300 perNode(stat, func(node *api.NodeCounters, val uint64) {
309 // GetInterfaceStats retrieves VPP per interface stats.
310 func (c *StatsConnection) GetInterfaceStats(ifaceStats *api.InterfaceStats) (err error) {
311 if err := c.updateStats(&c.ifaceStatsData, InterfaceStatsPrefix); err != nil {
315 prep := func(l int) {
316 if ifaceStats.Interfaces == nil || len(ifaceStats.Interfaces) != l {
317 ifaceStats.Interfaces = make([]api.InterfaceCounters, l)
318 for i := 0; i < l; i++ {
319 ifaceStats.Interfaces[i].InterfaceIndex = uint32(i)
323 perNode := func(stat adapter.StatEntry, fn func(*api.InterfaceCounters, uint64)) {
324 if s, ok := stat.Data.(adapter.SimpleCounterStat); ok {
326 for i := range ifaceStats.Interfaces {
327 val := adapter.ReduceSimpleCounterStatIndex(s, i)
328 fn(&ifaceStats.Interfaces[i], val)
332 perNodeComb := func(stat adapter.StatEntry, fn func(*api.InterfaceCounters, [2]uint64)) {
333 if s, ok := stat.Data.(adapter.CombinedCounterStat); ok {
335 for i := range ifaceStats.Interfaces {
336 val := adapter.ReduceCombinedCounterStatIndex(s, i)
337 fn(&ifaceStats.Interfaces[i], val)
342 for _, stat := range c.ifaceStatsData.Entries {
343 switch string(stat.Name) {
344 case InterfaceStats_Names:
345 if stat, ok := stat.Data.(adapter.NameStat); ok {
347 for i, nc := range ifaceStats.Interfaces {
348 if nc.InterfaceName != string(stat[i]) {
349 nc.InterfaceName = string(stat[i])
350 ifaceStats.Interfaces[i] = nc
354 case InterfaceStats_Drops:
355 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
358 case InterfaceStats_Punt:
359 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
362 case InterfaceStats_IP4:
363 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
366 case InterfaceStats_IP6:
367 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
370 case InterfaceStats_RxNoBuf:
371 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
374 case InterfaceStats_RxMiss:
375 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
378 case InterfaceStats_RxError:
379 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
382 case InterfaceStats_TxError:
383 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
386 case InterfaceStats_Mpls:
387 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
390 case InterfaceStats_Rx:
391 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
392 iface.Rx.Packets = val[0]
393 iface.Rx.Bytes = val[1]
395 case InterfaceStats_RxUnicast:
396 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
397 iface.RxUnicast.Packets = val[0]
398 iface.RxUnicast.Bytes = val[1]
400 case InterfaceStats_RxMulticast:
401 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
402 iface.RxMulticast.Packets = val[0]
403 iface.RxMulticast.Bytes = val[1]
405 case InterfaceStats_RxBroadcast:
406 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
407 iface.RxBroadcast.Packets = val[0]
408 iface.RxBroadcast.Bytes = val[1]
410 case InterfaceStats_Tx:
411 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
412 iface.Tx.Packets = val[0]
413 iface.Tx.Bytes = val[1]
415 case InterfaceStats_TxUnicastMiss:
416 // tx-unicast-miss was a spelling mistake in older versions
419 case InterfaceStats_TxUnicast:
420 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
421 iface.TxUnicast.Packets = val[0]
422 iface.TxUnicast.Bytes = val[1]
424 case InterfaceStats_TxMulticast:
425 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
426 iface.TxMulticast.Packets = val[0]
427 iface.TxMulticast.Bytes = val[1]
429 case InterfaceStats_TxBroadcast:
430 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
431 iface.TxBroadcast.Packets = val[0]
432 iface.TxBroadcast.Bytes = val[1]
440 // GetBufferStats retrieves VPP buffer pools stats.
441 func (c *StatsConnection) GetBufferStats(bufStats *api.BufferStats) (err error) {
442 if err := c.updateStats(&c.bufStatsData, BufferStatsPrefix); err != nil {
446 if bufStats.Buffer == nil {
447 bufStats.Buffer = make(map[string]api.BufferPool)
450 for _, stat := range c.bufStatsData.Entries {
451 d, f := path.Split(string(stat.Name))
452 d = strings.TrimSuffix(d, "/")
454 name := strings.TrimPrefix(d, BufferStatsPrefix)
455 b, ok := bufStats.Buffer[name]
461 s, ok := stat.Data.(adapter.ScalarStat)
466 case BufferStats_Cached:
468 case BufferStats_Used:
470 case BufferStats_Available:
474 bufStats.Buffer[name] = b
480 func (c *StatsConnection) GetMemoryStats(memStats *api.MemoryStats) (err error) {
481 if err := c.updateStats(&c.memStatsData, MemoryStatPrefix); err != nil {
485 for _, stat := range c.memStatsData.Entries {
486 _, f := path.Split(string(stat.Name))
488 m, ok := stat.Data.(adapter.ScalarStat)
493 case MemoryStats_Total:
495 case MemoryStats_Used: