Add statsclient options and fix wait for socket
[govpp.git] / examples / stats-client / stats_api.go
1 // Copyright (c) 2018 Cisco and/or its affiliates.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package main
16
17 import (
18         "flag"
19         "fmt"
20         "log"
21         "os"
22         "strings"
23         "time"
24
25         "git.fd.io/govpp.git/adapter"
26         "git.fd.io/govpp.git/adapter/statsclient"
27         "git.fd.io/govpp.git/api"
28         "git.fd.io/govpp.git/core"
29 )
30
31 // ------------------------------------------------------------------
32 // Example - Stats API
33 // ------------------------------------------------------------------
34 // The example stats_api demonstrates how to retrieve stats
35 // from the VPP using the new stats API.
36 // ------------------------------------------------------------------
37
38 var (
39         statsSocket = flag.String("socket", statsclient.DefaultSocketName, "Path to VPP stats socket")
40         dumpAll     = flag.Bool("all", false, "Dump all stats including ones with zero values")
41         pollPeriod  = flag.Duration("period", time.Second*5, "Polling interval period")
42         async       = flag.Bool("async", false, "Use asynchronous connection")
43 )
44
45 func init() {
46         flag.Usage = func() {
47                 fmt.Fprintf(os.Stderr, "%s: usage [ls|dump|poll|errors|interfaces|nodes|system|buffers|memory] <patterns>...\n", os.Args[0])
48                 flag.PrintDefaults()
49                 os.Exit(1)
50         }
51 }
52
53 func main() {
54         flag.Parse()
55         skipZeros := !*dumpAll
56
57         var patterns []string
58         if flag.NArg() > 0 {
59                 patterns = flag.Args()[1:]
60         }
61
62         var (
63                 client *statsclient.StatsClient
64                 c      *core.StatsConnection
65                 err    error
66         )
67
68         if *async {
69                 var statsChan chan core.ConnectionEvent
70                 client = statsclient.NewStatsClient(*statsSocket, statsclient.SetSocketRetryPeriod(1*time.Second),
71                         statsclient.SetSocketRetryTimeout(10*time.Second))
72                 c, statsChan, err = core.AsyncConnectStats(client, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
73                 if err != nil {
74                         log.Fatalln("Asynchronous connecting failed:", err)
75                 }
76                 select {
77                 case e := <-statsChan:
78                         if e.State == core.Connected {
79                                 // OK
80                         } else {
81                                 log.Fatalf("VPP stats asynchronous connection failed: %s\n", e.State.String())
82                         }
83                 }
84         } else {
85                 client = statsclient.NewStatsClient(*statsSocket)
86                 c, err = core.ConnectStats(client)
87                 if err != nil {
88                         log.Fatalln("Connecting failed:", err)
89                 }
90         }
91         defer c.Disconnect()
92
93         switch cmd := flag.Arg(0); cmd {
94         case "system":
95                 stats := new(api.SystemStats)
96                 if err := c.GetSystemStats(stats); err != nil {
97                         log.Fatalln("getting system stats failed:", err)
98                 }
99                 fmt.Printf("System stats: %+v\n", stats)
100
101         case "poll-system":
102                 pollSystem(c)
103
104         case "nodes":
105                 fmt.Println("Listing node stats..")
106                 stats := new(api.NodeStats)
107                 if err := c.GetNodeStats(stats); err != nil {
108                         log.Fatalln("getting node stats failed:", err)
109                 }
110
111                 for _, node := range stats.Nodes {
112                         if skipZeros && node.Calls == 0 && node.Suspends == 0 && node.Clocks == 0 && node.Vectors == 0 {
113                                 continue
114                         }
115                         fmt.Printf(" - %+v\n", node)
116                 }
117                 fmt.Printf("Listed %d node counters\n", len(stats.Nodes))
118
119         case "interfaces":
120                 fmt.Println("Listing interface stats..")
121                 stats := new(api.InterfaceStats)
122                 if err := c.GetInterfaceStats(stats); err != nil {
123                         log.Fatalln("getting interface stats failed:", err)
124                 }
125                 for _, iface := range stats.Interfaces {
126                         fmt.Printf(" - %+v\n", iface)
127                 }
128                 fmt.Printf("Listed %d interface counters\n", len(stats.Interfaces))
129
130         case "poll-interfaces":
131                 pollInterfaces(c)
132
133         case "errors":
134                 fmt.Printf("Listing error stats.. %s\n", strings.Join(patterns, " "))
135                 stats := new(api.ErrorStats)
136                 if err := c.GetErrorStats(stats); err != nil {
137                         log.Fatalln("getting error stats failed:", err)
138                 }
139                 n := 0
140                 for _, counter := range stats.Errors {
141                         var sum uint32
142                         for _, valuePerWorker := range counter.Values {
143                                 sum += uint32(valuePerWorker)
144                         }
145
146                         if skipZeros && sum == 0 {
147                                 continue
148                         }
149                         fmt.Printf(" - %v %d (per worker: %v)\n", counter.CounterName, sum, counter.Values)
150                         n++
151                 }
152                 fmt.Printf("Listed %d (%d) error counters\n", n, len(stats.Errors))
153
154         case "buffers":
155                 stats := new(api.BufferStats)
156                 if err := c.GetBufferStats(stats); err != nil {
157                         log.Fatalln("getting buffer stats failed:", err)
158                 }
159                 fmt.Printf("Buffer stats: %+v\n", stats)
160
161         case "memory":
162                 stats := new(api.MemoryStats)
163                 if err := c.GetMemoryStats(stats); err != nil {
164                         log.Fatalln("getting memory stats failed:", err)
165                 }
166                 fmt.Printf("Memory stats: %+v\n", stats)
167
168         case "dump":
169                 fmt.Printf("Dumping stats.. %s\n", strings.Join(patterns, " "))
170
171                 dumpStats(client, patterns, skipZeros)
172
173         case "poll":
174                 fmt.Printf("Polling stats.. %s\n", strings.Join(patterns, " "))
175
176                 pollStats(client, patterns, skipZeros)
177
178         case "list", "ls", "":
179                 fmt.Printf("Listing stats.. %s\n", strings.Join(patterns, " "))
180
181                 listStats(client, patterns)
182
183         default:
184                 fmt.Printf("invalid command: %q\n", cmd)
185         }
186 }
187
188 func listStats(client adapter.StatsAPI, patterns []string) {
189         list, err := client.ListStats(patterns...)
190         if err != nil {
191                 log.Fatalln("listing stats failed:", err)
192         }
193
194         for _, stat := range list {
195                 fmt.Printf(" - %v\n", stat)
196         }
197
198         fmt.Printf("Listed %d stats\n", len(list))
199 }
200
201 func dumpStats(client adapter.StatsAPI, patterns []string, skipZeros bool) {
202         stats, err := client.DumpStats(patterns...)
203         if err != nil {
204                 log.Fatalln("dumping stats failed:", err)
205         }
206
207         n := 0
208         for _, stat := range stats {
209                 if skipZeros && (stat.Data == nil || stat.Data.IsZero()) {
210                         continue
211                 }
212                 fmt.Printf(" - %-50s %25v %+v\n", stat.Name, stat.Type, stat.Data)
213                 n++
214         }
215
216         fmt.Printf("Dumped %d (%d) stats\n", n, len(stats))
217 }
218
219 func pollStats(client adapter.StatsAPI, patterns []string, skipZeros bool) {
220         dir, err := client.PrepareDir(patterns...)
221         if err != nil {
222                 log.Fatalln("preparing dir failed:", err)
223         }
224
225         tick := time.Tick(*pollPeriod)
226         for {
227                 n := 0
228                 fmt.Println(time.Now().Format(time.Stamp))
229                 for _, stat := range dir.Entries {
230                         if skipZeros && (stat.Data == nil || stat.Data.IsZero()) {
231                                 continue
232                         }
233                         fmt.Printf("%-50s %+v\n", stat.Name, stat.Data)
234                         n++
235                 }
236                 fmt.Println()
237
238                 select {
239                 case <-tick:
240                         if err := client.UpdateDir(dir); err != nil {
241                                 if err == adapter.ErrStatsDirStale {
242                                         if dir, err = client.PrepareDir(patterns...); err != nil {
243                                                 log.Fatalln("preparing dir failed:", err)
244                                         }
245                                         continue
246                                 }
247                                 log.Fatalln("updating dir failed:", err)
248                         }
249                 }
250         }
251 }
252
253 func pollSystem(client api.StatsProvider) {
254         stats := new(api.SystemStats)
255
256         if err := client.GetSystemStats(stats); err != nil {
257                 log.Fatalln("updating system stats failed:", err)
258         }
259
260         tick := time.Tick(*pollPeriod)
261         for {
262                 fmt.Printf("System stats: %+v\n", stats)
263                 fmt.Println()
264
265                 select {
266                 case <-tick:
267                         if err := client.GetSystemStats(stats); err != nil {
268                                 log.Println("updating system stats failed:", err)
269                         }
270                 }
271         }
272 }
273
274 func pollInterfaces(client api.StatsProvider) {
275         stats := new(api.InterfaceStats)
276
277         if err := client.GetInterfaceStats(stats); err != nil {
278                 log.Fatalln("updating system stats failed:", err)
279         }
280
281         tick := time.Tick(*pollPeriod)
282         for {
283                 fmt.Printf("Interface stats (%d interfaces)\n", len(stats.Interfaces))
284                 for i := range stats.Interfaces {
285                         fmt.Printf(" - %+v\n", stats.Interfaces[i])
286                 }
287                 fmt.Println()
288
289                 select {
290                 case <-tick:
291                         if err := client.GetInterfaceStats(stats); err != nil {
292                                 log.Println("updating system stats failed:", err)
293                         }
294                 }
295         }
296 }