stats: golang vpp_if_stats_client
[vpp.git] / extras / vpp_if_stats / vpp_if_stats.go
1 package main
2
3 import (
4         "flag"
5         "fmt"
6         "git.fd.io/govpp.git"
7         "git.fd.io/govpp.git/adapter"
8         "git.fd.io/govpp.git/adapter/vppapiclient"
9         "git.fd.io/govpp.git/api"
10         "git.fd.io/govpp.git/core"
11         "git.fd.io/govpp.git/examples/bin_api/interfaces"
12         "git.fd.io/govpp.git/examples/bin_api/vpe"
13         "log"
14 )
15
16 //////////////////////////////////////
17 /////////   Data structs   ///////////
18 //////////////////////////////////////
19
20 const defaultStatsSocketPath = "/run/vpp/stats.sock"
21 const defaultShmPrefix = ""
22
23 func parseMacAddress(l2Address []byte, l2AddressLength uint32) string {
24         var mac string
25         for i := uint32(0); i < l2AddressLength; i++ {
26                 mac += fmt.Sprintf("%02x", l2Address[i])
27                 if i < l2AddressLength-1 {
28                         mac += ":"
29                 }
30         }
31         return mac
32 }
33
34 type interfaceStats struct {
35         TxBytes   uint64
36         TxPackets uint64
37         TxErrors  uint64
38         RxBytes   uint64
39         RxPackets uint64
40         RxErrors  uint64
41         Drops     uint64
42         Punts     uint64
43 }
44
45 type vppInterface struct {
46         interfaces.SwInterfaceDetails
47         Stats interfaceStats
48 }
49
50 type vppConnector struct {
51         statsSocketPath string
52         shmPrefix       string
53
54         conn  *core.Connection
55         api   api.Channel
56         stats adapter.StatsAPI
57
58         VppDetails vpe.ShowVersionReply
59         Interfaces []*vppInterface
60 }
61
62 //////////////////////////////////////
63 /////////   VPP workflow   ///////////
64 //////////////////////////////////////
65
66 func (v *vppConnector) getVppVersion() error {
67         if err := v.api.SendRequest(&vpe.ShowVersion{}).ReceiveReply(&v.VppDetails); err != nil {
68                 return fmt.Errorf("failed to fetch vpp version: %v", err)
69         }
70         return nil
71 }
72
73 func (v *vppConnector) getInterfaces() error {
74         ifCtx := v.api.SendMultiRequest(&interfaces.SwInterfaceDump{})
75         for {
76                 ifDetails := interfaces.SwInterfaceDetails{}
77                 stop, err := ifCtx.ReceiveReply(&ifDetails)
78                 if err != nil {
79                         return fmt.Errorf("failed to fetch vpp interface: %v", err)
80                 }
81                 if stop {
82                         break
83                 }
84
85                 v.Interfaces = append(v.Interfaces, &vppInterface{SwInterfaceDetails: ifDetails})
86         }
87         return nil
88 }
89
90 func (v *vppConnector) connect() (err error) {
91         if v.conn, err = govpp.Connect(v.shmPrefix); err != nil {
92                 return fmt.Errorf("failed to connect to vpp: %v", err)
93         }
94
95         if v.api, err = v.conn.NewAPIChannel(); err != nil {
96                 return fmt.Errorf("failed to create api channel: %v", err)
97         }
98
99         v.stats = vppapiclient.NewStatClient(v.statsSocketPath)
100         if err = v.stats.Connect(); err != nil {
101                 return fmt.Errorf("failed to connect to Stats adapter: %v", err)
102         }
103
104         return
105 }
106
107 func (v *vppConnector) disconnect() {
108         if v.stats != nil {
109                 v.stats.Disconnect()
110         }
111         if v.conn != nil {
112                 v.conn.Disconnect()
113         }
114 }
115
116 func (v *vppConnector) reduceCombinedCounters(stat *adapter.StatEntry) *[]adapter.CombinedCounter {
117         counters := stat.Data.(adapter.CombinedCounterStat)
118         stats := make([]adapter.CombinedCounter, len(v.Interfaces))
119         for _, workerStats := range counters {
120                 for i, interfaceStats := range workerStats {
121                         stats[i].Bytes += interfaceStats.Bytes
122                         stats[i].Packets += interfaceStats.Packets
123                 }
124         }
125         return &stats
126 }
127
128 func (v *vppConnector) reduceSimpleCounters(stat *adapter.StatEntry) *[]adapter.Counter {
129         counters := stat.Data.(adapter.SimpleCounterStat)
130         stats := make([]adapter.Counter, len(v.Interfaces))
131         for _, workerStats := range counters {
132                 for i, interfaceStats := range workerStats {
133                         stats[i] += interfaceStats
134                 }
135         }
136         return &stats
137 }
138
139 func (v *vppConnector) getStatsForAllInterfaces() error {
140         statsDump, err := v.stats.DumpStats("/if")
141         if err != nil {
142                 return fmt.Errorf("failed to dump vpp Stats: %v", err)
143         }
144
145         stats := func(i int) *interfaceStats { return &v.Interfaces[uint32(i)].Stats }
146
147         for _, stat := range statsDump {
148                 switch stat.Name {
149                 case "/if/tx":
150                         {
151                                 for i, counter := range *v.reduceCombinedCounters(stat) {
152                                         stats(i).TxBytes = uint64(counter.Bytes)
153                                         stats(i).TxPackets = uint64(counter.Packets)
154                                 }
155                         }
156                 case "/if/rx":
157                         {
158                                 for i, counter := range *v.reduceCombinedCounters(stat) {
159                                         stats(i).RxBytes = uint64(counter.Bytes)
160                                         stats(i).RxPackets = uint64(counter.Packets)
161                                 }
162                         }
163                 case "/if/tx-error":
164                         {
165                                 for i, counter := range *v.reduceSimpleCounters(stat) {
166                                         stats(i).TxErrors = uint64(counter)
167                                 }
168                         }
169                 case "/if/rx-error":
170                         {
171                                 for i, counter := range *v.reduceSimpleCounters(stat) {
172                                         stats(i).RxErrors = uint64(counter)
173                                 }
174                         }
175                 case "/if/drops":
176                         {
177                                 for i, counter := range *v.reduceSimpleCounters(stat) {
178                                         stats(i).Drops = uint64(counter)
179                                 }
180                         }
181                 case "/if/punt":
182                         {
183                                 for i, counter := range *v.reduceSimpleCounters(stat) {
184                                         stats(i).Punts = uint64(counter)
185                                 }
186                         }
187                 }
188         }
189         return nil
190 }
191
192 //////////////////////////////////////
193
194 func main() {
195         statsSocketPathPtr := flag.String("stats_socket_path", defaultStatsSocketPath, "Path to vpp stats socket")
196         shmPrefixPtr := flag.String("shm_prefix", defaultShmPrefix, "Shared memory prefix (advanced)")
197         flag.Parse()
198
199         vppConn := &vppConnector{statsSocketPath: *statsSocketPathPtr, shmPrefix: *shmPrefixPtr}
200         defer vppConn.disconnect()
201
202         if err := vppConn.connect(); err != nil {
203                 log.Fatalln(err)
204         }
205
206         if err := vppConn.getVppVersion(); err != nil {
207                 log.Fatalln(err)
208         }
209
210         if err := vppConn.getInterfaces(); err != nil {
211                 log.Fatalln(err)
212         }
213
214         if err := vppConn.getStatsForAllInterfaces(); err != nil {
215                 log.Fatalln(err)
216         }
217
218         jsonString, err := dumpToJSONString(vppConn)
219         if err != nil {
220                 log.Fatalln(err)
221         }
222         fmt.Println(jsonString)
223 }