blob: 90c1700430c908bf5ec1576c68de2ff4098cff22 [file] [log] [blame]
Koren Lev19ca78f2018-11-21 18:46:54 +02001package main
2
3import (
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
20const defaultStatsSocketPath = "/run/vpp/stats.sock"
21const defaultShmPrefix = ""
22
23func 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
34type 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
45type vppInterface struct {
46 interfaces.SwInterfaceDetails
47 Stats interfaceStats
48}
49
50type 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
66func (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
73func (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
90func (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
107func (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
116func (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 {
Koren Levf3b7a5e2018-12-06 10:51:18 +0200120 for i := 0; i < len(v.Interfaces); i++ {
121 stats[i].Bytes += workerStats[i].Bytes
122 stats[i].Packets += workerStats[i].Packets
Koren Lev19ca78f2018-11-21 18:46:54 +0200123 }
124 }
125 return &stats
126}
127
128func (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 {
Koren Levf3b7a5e2018-12-06 10:51:18 +0200132 for i := 0; i < len(v.Interfaces); i++ {
133 stats[i] += workerStats[i]
Koren Lev19ca78f2018-11-21 18:46:54 +0200134 }
135 }
136 return &stats
137}
138
139func (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
194func 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}