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 InterfaceStatsPrefix = "/if/"
43 InterfaceStats_Names = InterfaceStatsPrefix + "names"
44 InterfaceStats_Drops = InterfaceStatsPrefix + "drops"
45 InterfaceStats_Punt = InterfaceStatsPrefix + "punt"
46 InterfaceStats_IP4 = InterfaceStatsPrefix + "ip4"
47 InterfaceStats_IP6 = InterfaceStatsPrefix + "ip6"
48 InterfaceStats_RxNoBuf = InterfaceStatsPrefix + "rx-no-buf"
49 InterfaceStats_RxMiss = InterfaceStatsPrefix + "rx-miss"
50 InterfaceStats_RxError = InterfaceStatsPrefix + "rx-error"
51 InterfaceStats_TxError = InterfaceStatsPrefix + "tx-error"
52 InterfaceStats_Mpls = InterfaceStatsPrefix + "mpls"
53 InterfaceStats_Rx = InterfaceStatsPrefix + "rx"
54 InterfaceStats_RxUnicast = InterfaceStatsPrefix + "rx-unicast"
55 InterfaceStats_RxMulticast = InterfaceStatsPrefix + "rx-multicast"
56 InterfaceStats_RxBroadcast = InterfaceStatsPrefix + "rx-broadcast"
57 InterfaceStats_Tx = InterfaceStatsPrefix + "tx"
58 InterfaceStats_TxUnicast = InterfaceStatsPrefix + "tx-unicast"
59 InterfaceStats_TxUnicastMiss = InterfaceStatsPrefix + "tx-unicast-miss"
60 InterfaceStats_TxMulticast = InterfaceStatsPrefix + "tx-multicast"
61 InterfaceStats_TxBroadcast = InterfaceStatsPrefix + "tx-broadcast"
63 // TODO: network stats
64 NetworkStatsPrefix = "/net/"
65 NetworkStats_RouteTo = NetworkStatsPrefix + "route/to"
66 NetworkStats_RouteVia = NetworkStatsPrefix + "route/via"
67 NetworkStats_MRoute = NetworkStatsPrefix + "mroute"
68 NetworkStats_Adjacency = NetworkStatsPrefix + "adjacency"
69 NetworkStats_Punt = NetworkStatsPrefix + "punt"
72 type StatsConnection struct {
73 statsClient adapter.StatsAPI
75 // connected is true if the adapter is connected to VPP
78 errorStatsData *adapter.StatDir
79 nodeStatsData *adapter.StatDir
80 ifaceStatsData *adapter.StatDir
81 sysStatsData *adapter.StatDir
82 bufStatsData *adapter.StatDir
85 func newStatsConnection(stats adapter.StatsAPI) *StatsConnection {
86 return &StatsConnection{
91 // ConnectStats connects to Stats API using specified adapter and returns a connection handle.
92 // This call blocks until it is either connected, or an error occurs.
93 // Only one connection attempt will be performed.
94 func ConnectStats(stats adapter.StatsAPI) (*StatsConnection, error) {
95 c := newStatsConnection(stats)
97 if err := c.connectClient(); err != nil {
104 func (c *StatsConnection) connectClient() error {
105 log.Debug("Connecting to stats..")
107 if err := c.statsClient.Connect(); err != nil {
111 log.Debugf("Connected to stats.")
113 // store connected state
114 atomic.StoreUint32(&c.connected, 1)
119 // Disconnect disconnects from Stats API and releases all connection-related resources.
120 func (c *StatsConnection) Disconnect() {
124 if c.statsClient != nil {
129 func (c *StatsConnection) disconnectClient() {
130 if atomic.CompareAndSwapUint32(&c.connected, 1, 0) {
131 if err := c.statsClient.Disconnect(); err != nil {
132 log.Debugf("disconnecting stats client failed: %v", err)
137 func (c *StatsConnection) updateStats(statDir **adapter.StatDir, patterns ...string) error {
139 panic("statDir must not nil")
141 try := func() error {
142 if (*statDir) == nil {
143 dir, err := c.statsClient.PrepareDir(patterns...)
145 log.Debugln("preparing dir failed:", err)
150 if err := c.statsClient.UpdateDir(*statDir); err != nil {
151 log.Debugln("updating dir failed:", err)
160 for r := 0; r < RetryUpdateCount; r++ {
161 if err = try(); err == nil {
163 log.Debugf("retry successfull (r=%d)", r)
166 } else if err == adapter.ErrStatsDirStale || err == adapter.ErrStatsDataBusy {
169 log.Debugf("sleeping for %v before next try", RetryUpdateDelay)
170 time.Sleep(RetryUpdateDelay)
173 // error is not retryable
180 // GetSystemStats retrieves VPP system stats.
181 func (c *StatsConnection) GetSystemStats(sysStats *api.SystemStats) (err error) {
182 if err := c.updateStats(&c.sysStatsData, SystemStatsPrefix); err != nil {
186 for _, stat := range c.sysStatsData.Entries {
188 if s, ok := stat.Data.(adapter.ScalarStat); ok {
191 switch string(stat.Name) {
192 case SystemStats_VectorRate:
193 sysStats.VectorRate = val
194 case SystemStats_NumWorkerThreads:
195 sysStats.NumWorkerThreads = val
196 case SystemStats_VectorRatePerWorker:
198 if ss, ok := stat.Data.(adapter.SimpleCounterStat); ok {
199 vals = make([]uint64, len(ss))
204 vals[w] = uint64(ss[w][0])
207 sysStats.VectorRatePerWorker = vals
208 case SystemStats_InputRate:
209 sysStats.InputRate = val
210 case SystemStats_LastUpdate:
211 sysStats.LastUpdate = val
212 case SystemStats_LastStatsClear:
213 sysStats.LastStatsClear = val
214 case SystemStats_Heartbeat:
215 sysStats.Heartbeat = val
222 // GetErrorStats retrieves VPP error stats.
223 func (c *StatsConnection) GetErrorStats(errorStats *api.ErrorStats) (err error) {
224 if err := c.updateStats(&c.errorStatsData, CounterStatsPrefix); err != nil {
228 if errorStats.Errors == nil || len(errorStats.Errors) != len(c.errorStatsData.Entries) {
229 errorStats.Errors = make([]api.ErrorCounter, len(c.errorStatsData.Entries))
230 for i := 0; i < len(c.errorStatsData.Entries); i++ {
231 errorStats.Errors[i].CounterName = string(c.errorStatsData.Entries[i].Name)
235 for i, stat := range c.errorStatsData.Entries {
236 if stat.Type != adapter.ErrorIndex {
239 if errStat, ok := stat.Data.(adapter.ErrorStat); ok {
240 errorStats.Errors[i].Value = uint64(errStat)
247 func (c *StatsConnection) GetNodeStats(nodeStats *api.NodeStats) (err error) {
248 if err := c.updateStats(&c.nodeStatsData, NodeStatsPrefix); err != nil {
252 prepNodes := func(l int) {
253 if nodeStats.Nodes == nil || len(nodeStats.Nodes) != l {
254 nodeStats.Nodes = make([]api.NodeCounters, l)
255 for i := 0; i < l; i++ {
256 nodeStats.Nodes[i].NodeIndex = uint32(i)
260 perNode := func(stat adapter.StatEntry, fn func(*api.NodeCounters, uint64)) {
261 if s, ok := stat.Data.(adapter.SimpleCounterStat); ok {
263 for i := range nodeStats.Nodes {
264 val := adapter.ReduceSimpleCounterStatIndex(s, i)
265 fn(&nodeStats.Nodes[i], val)
270 for _, stat := range c.nodeStatsData.Entries {
271 switch string(stat.Name) {
272 case NodeStats_Names:
273 if stat, ok := stat.Data.(adapter.NameStat); ok {
275 for i, nc := range nodeStats.Nodes {
276 if nc.NodeName != string(stat[i]) {
277 nc.NodeName = string(stat[i])
278 nodeStats.Nodes[i] = nc
282 case NodeStats_Clocks:
283 perNode(stat, func(node *api.NodeCounters, val uint64) {
286 case NodeStats_Vectors:
287 perNode(stat, func(node *api.NodeCounters, val uint64) {
290 case NodeStats_Calls:
291 perNode(stat, func(node *api.NodeCounters, val uint64) {
294 case NodeStats_Suspends:
295 perNode(stat, func(node *api.NodeCounters, val uint64) {
304 // GetInterfaceStats retrieves VPP per interface stats.
305 func (c *StatsConnection) GetInterfaceStats(ifaceStats *api.InterfaceStats) (err error) {
306 if err := c.updateStats(&c.ifaceStatsData, InterfaceStatsPrefix); err != nil {
310 prep := func(l int) {
311 if ifaceStats.Interfaces == nil || len(ifaceStats.Interfaces) != l {
312 ifaceStats.Interfaces = make([]api.InterfaceCounters, l)
313 for i := 0; i < l; i++ {
314 ifaceStats.Interfaces[i].InterfaceIndex = uint32(i)
318 perNode := func(stat adapter.StatEntry, fn func(*api.InterfaceCounters, uint64)) {
319 if s, ok := stat.Data.(adapter.SimpleCounterStat); ok {
321 for i := range ifaceStats.Interfaces {
322 val := adapter.ReduceSimpleCounterStatIndex(s, i)
323 fn(&ifaceStats.Interfaces[i], val)
327 perNodeComb := func(stat adapter.StatEntry, fn func(*api.InterfaceCounters, [2]uint64)) {
328 if s, ok := stat.Data.(adapter.CombinedCounterStat); ok {
330 for i := range ifaceStats.Interfaces {
331 val := adapter.ReduceCombinedCounterStatIndex(s, i)
332 fn(&ifaceStats.Interfaces[i], val)
337 for _, stat := range c.ifaceStatsData.Entries {
338 switch string(stat.Name) {
339 case InterfaceStats_Names:
340 if stat, ok := stat.Data.(adapter.NameStat); ok {
342 for i, nc := range ifaceStats.Interfaces {
343 if nc.InterfaceName != string(stat[i]) {
344 nc.InterfaceName = string(stat[i])
345 ifaceStats.Interfaces[i] = nc
349 case InterfaceStats_Drops:
350 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
353 case InterfaceStats_Punt:
354 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
357 case InterfaceStats_IP4:
358 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
361 case InterfaceStats_IP6:
362 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
365 case InterfaceStats_RxNoBuf:
366 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
369 case InterfaceStats_RxMiss:
370 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
373 case InterfaceStats_RxError:
374 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
377 case InterfaceStats_TxError:
378 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
381 case InterfaceStats_Mpls:
382 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
385 case InterfaceStats_Rx:
386 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
387 iface.Rx.Packets = val[0]
388 iface.Rx.Bytes = val[1]
390 case InterfaceStats_RxUnicast:
391 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
392 iface.RxUnicast.Packets = val[0]
393 iface.RxUnicast.Bytes = val[1]
395 case InterfaceStats_RxMulticast:
396 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
397 iface.RxMulticast.Packets = val[0]
398 iface.RxMulticast.Bytes = val[1]
400 case InterfaceStats_RxBroadcast:
401 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
402 iface.RxBroadcast.Packets = val[0]
403 iface.RxBroadcast.Bytes = val[1]
405 case InterfaceStats_Tx:
406 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
407 iface.Tx.Packets = val[0]
408 iface.Tx.Bytes = val[1]
410 case InterfaceStats_TxUnicastMiss:
411 // tx-unicast-miss was a spelling mistake in older versions
414 case InterfaceStats_TxUnicast:
415 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
416 iface.TxUnicast.Packets = val[0]
417 iface.TxUnicast.Bytes = val[1]
419 case InterfaceStats_TxMulticast:
420 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
421 iface.TxMulticast.Packets = val[0]
422 iface.TxMulticast.Bytes = val[1]
424 case InterfaceStats_TxBroadcast:
425 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
426 iface.TxBroadcast.Packets = val[0]
427 iface.TxBroadcast.Bytes = val[1]
435 // GetBufferStats retrieves VPP buffer pools stats.
436 func (c *StatsConnection) GetBufferStats(bufStats *api.BufferStats) (err error) {
437 if err := c.updateStats(&c.bufStatsData, BufferStatsPrefix); err != nil {
441 if bufStats.Buffer == nil {
442 bufStats.Buffer = make(map[string]api.BufferPool)
445 for _, stat := range c.bufStatsData.Entries {
446 d, f := path.Split(string(stat.Name))
447 d = strings.TrimSuffix(d, "/")
449 name := strings.TrimPrefix(d, BufferStatsPrefix)
450 b, ok := bufStats.Buffer[name]
456 s, ok := stat.Data.(adapter.ScalarStat)
461 case BufferStats_Cached:
463 case BufferStats_Used:
465 case BufferStats_Available:
469 bufStats.Buffer[name] = b