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 // Connect 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.Debugln("sleeping for %v before next try", RetryUpdateDelay)
170 time.Sleep(RetryUpdateDelay)
173 // error is not retryable
180 // UpdateSystemStats 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))
201 vals[w] = uint64(ss[w][0])
204 sysStats.VectorRatePerWorker = vals
205 case SystemStats_InputRate:
206 sysStats.InputRate = val
207 case SystemStats_LastUpdate:
208 sysStats.LastUpdate = val
209 case SystemStats_LastStatsClear:
210 sysStats.LastStatsClear = val
211 case SystemStats_Heartbeat:
212 sysStats.Heartbeat = val
219 // GetErrorStats retrieves VPP error stats.
220 func (c *StatsConnection) GetErrorStats(errorStats *api.ErrorStats) (err error) {
221 if err := c.updateStats(&c.errorStatsData, CounterStatsPrefix); err != nil {
225 if errorStats.Errors == nil || len(errorStats.Errors) != len(c.errorStatsData.Entries) {
226 errorStats.Errors = make([]api.ErrorCounter, len(c.errorStatsData.Entries))
227 for i := 0; i < len(c.errorStatsData.Entries); i++ {
228 errorStats.Errors[i].CounterName = string(c.errorStatsData.Entries[i].Name)
232 for i, stat := range c.errorStatsData.Entries {
233 if stat.Type != adapter.ErrorIndex {
236 if errStat, ok := stat.Data.(adapter.ErrorStat); ok {
237 errorStats.Errors[i].Value = uint64(errStat)
244 func (c *StatsConnection) GetNodeStats(nodeStats *api.NodeStats) (err error) {
245 if err := c.updateStats(&c.nodeStatsData, NodeStatsPrefix); err != nil {
249 prepNodes := func(l int) {
250 if nodeStats.Nodes == nil || len(nodeStats.Nodes) != l {
251 nodeStats.Nodes = make([]api.NodeCounters, l)
252 for i := 0; i < l; i++ {
253 nodeStats.Nodes[i].NodeIndex = uint32(i)
257 perNode := func(stat adapter.StatEntry, fn func(*api.NodeCounters, uint64)) {
258 s := stat.Data.(adapter.SimpleCounterStat)
260 for i := range nodeStats.Nodes {
261 val := adapter.ReduceSimpleCounterStatIndex(s, i)
262 fn(&nodeStats.Nodes[i], val)
266 for _, stat := range c.nodeStatsData.Entries {
267 switch string(stat.Name) {
268 case NodeStats_Names:
269 stat := stat.Data.(adapter.NameStat)
271 for i, nc := range nodeStats.Nodes {
272 if nc.NodeName != string(stat[i]) {
273 nc.NodeName = string(stat[i])
274 nodeStats.Nodes[i] = nc
277 case NodeStats_Clocks:
278 perNode(stat, func(node *api.NodeCounters, val uint64) {
281 case NodeStats_Vectors:
282 perNode(stat, func(node *api.NodeCounters, val uint64) {
285 case NodeStats_Calls:
286 perNode(stat, func(node *api.NodeCounters, val uint64) {
289 case NodeStats_Suspends:
290 perNode(stat, func(node *api.NodeCounters, val uint64) {
299 // GetInterfaceStats retrieves VPP per interface stats.
300 func (c *StatsConnection) GetInterfaceStats(ifaceStats *api.InterfaceStats) (err error) {
301 if err := c.updateStats(&c.ifaceStatsData, InterfaceStatsPrefix); err != nil {
305 prep := func(l int) {
306 if ifaceStats.Interfaces == nil || len(ifaceStats.Interfaces) != l {
307 ifaceStats.Interfaces = make([]api.InterfaceCounters, l)
308 for i := 0; i < l; i++ {
309 ifaceStats.Interfaces[i].InterfaceIndex = uint32(i)
313 perNode := func(stat adapter.StatEntry, fn func(*api.InterfaceCounters, uint64)) {
314 s := stat.Data.(adapter.SimpleCounterStat)
316 for i := range ifaceStats.Interfaces {
317 val := adapter.ReduceSimpleCounterStatIndex(s, i)
318 fn(&ifaceStats.Interfaces[i], val)
321 perNodeComb := func(stat adapter.StatEntry, fn func(*api.InterfaceCounters, [2]uint64)) {
322 s := stat.Data.(adapter.CombinedCounterStat)
324 for i := range ifaceStats.Interfaces {
325 val := adapter.ReduceCombinedCounterStatIndex(s, i)
326 fn(&ifaceStats.Interfaces[i], val)
330 for _, stat := range c.ifaceStatsData.Entries {
331 switch string(stat.Name) {
332 case InterfaceStats_Names:
333 stat := stat.Data.(adapter.NameStat)
335 for i, nc := range ifaceStats.Interfaces {
336 if nc.InterfaceName != string(stat[i]) {
337 nc.InterfaceName = string(stat[i])
338 ifaceStats.Interfaces[i] = nc
341 case InterfaceStats_Drops:
342 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
345 case InterfaceStats_Punt:
346 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
349 case InterfaceStats_IP4:
350 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
353 case InterfaceStats_IP6:
354 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
357 case InterfaceStats_RxNoBuf:
358 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
361 case InterfaceStats_RxMiss:
362 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
365 case InterfaceStats_RxError:
366 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
369 case InterfaceStats_TxError:
370 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
373 case InterfaceStats_Mpls:
374 perNode(stat, func(iface *api.InterfaceCounters, val uint64) {
377 case InterfaceStats_Rx:
378 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
379 iface.Rx.Packets = val[0]
380 iface.Rx.Bytes = val[1]
382 case InterfaceStats_RxUnicast:
383 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
384 iface.RxUnicast.Packets = val[0]
385 iface.RxUnicast.Bytes = val[1]
387 case InterfaceStats_RxMulticast:
388 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
389 iface.RxMulticast.Packets = val[0]
390 iface.RxMulticast.Bytes = val[1]
392 case InterfaceStats_RxBroadcast:
393 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
394 iface.RxBroadcast.Packets = val[0]
395 iface.RxBroadcast.Bytes = val[1]
397 case InterfaceStats_Tx:
398 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
399 iface.Tx.Packets = val[0]
400 iface.Tx.Bytes = val[1]
402 case InterfaceStats_TxUnicastMiss:
403 // tx-unicast-miss was a spelling mistake in older versions
406 case InterfaceStats_TxUnicast:
407 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
408 iface.TxUnicast.Packets = val[0]
409 iface.TxUnicast.Bytes = val[1]
411 case InterfaceStats_TxMulticast:
412 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
413 iface.TxMulticast.Packets = val[0]
414 iface.TxMulticast.Bytes = val[1]
416 case InterfaceStats_TxBroadcast:
417 perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) {
418 iface.TxBroadcast.Packets = val[0]
419 iface.TxBroadcast.Bytes = val[1]
427 // GetBufferStats retrieves VPP buffer pools stats.
428 func (c *StatsConnection) GetBufferStats(bufStats *api.BufferStats) (err error) {
429 if err := c.updateStats(&c.bufStatsData, BufferStatsPrefix); err != nil {
433 if bufStats.Buffer == nil {
434 bufStats.Buffer = make(map[string]api.BufferPool)
437 for _, stat := range c.bufStatsData.Entries {
438 d, f := path.Split(string(stat.Name))
439 d = strings.TrimSuffix(d, "/")
441 name := strings.TrimPrefix(d, BufferStatsPrefix)
442 b, ok := bufStats.Buffer[name]
448 s, ok := stat.Data.(adapter.ScalarStat)
453 case BufferStats_Cached:
455 case BufferStats_Used:
457 case BufferStats_Available:
461 bufStats.Buffer[name] = b