summaryrefslogtreecommitdiffstats
path: root/examples/stats-client
diff options
context:
space:
mode:
authorOndrej Fabry <ofabry@cisco.com>2019-09-17 12:41:47 +0200
committerOndrej Fabry <ofabry@cisco.com>2019-10-03 12:54:22 +0200
commit809b69ea4a90455445c34bbad7d8e5fea5cf3462 (patch)
treeba4b94339ac83908e2d9933692dc373929380aa7 /examples/stats-client
parent48198748bdfcc7d30c794cdac19de822da53f840 (diff)
Optimizations for statsclient
- this dramatically improves performance for stats data collection - memory allocation is now done only when stat dirs change - updating prepared stat dir does not need to allocate memory - created integration test for testing stats client - added NumWorkerThreads and VectorRatePerWorker to SystemStats - added ReduceSimpleCounterStatIndex, ReduceCombinedCounterStatIndex for aggregating specific index Change-Id: I702731a69024ab5dd0832bb5cfe2773a987359e5 Signed-off-by: Ondrej Fabry <ofabry@cisco.com>
Diffstat (limited to 'examples/stats-client')
-rw-r--r--examples/stats-client/README.md150
-rw-r--r--examples/stats-client/stats_api.go260
2 files changed, 410 insertions, 0 deletions
diff --git a/examples/stats-client/README.md b/examples/stats-client/README.md
new file mode 100644
index 0000000..0a44a55
--- /dev/null
+++ b/examples/stats-client/README.md
@@ -0,0 +1,150 @@
+# Stats Client Example
+
+This example demonstrates how to retrieve statistics from VPP using [the new Stats API](https://github.com/FDio/vpp/blob/master/src/vpp/stats/stats.md).
+
+## Requirements
+
+The following requirements are required to run this example:
+
+- install VPP **18.10+** (VPP 19.04+ for statsclient)
+- enable stats in VPP
+
+To enable stats add following section to you VPP config:
+
+ ```sh
+ statseg {
+ default
+ per-node-counters on
+ }
+ ```
+ > The [default socket](https://wiki.fd.io/view/VPP/Command-line_Arguments#.22statseg.22_parameters) is located at `/run/vpp/stats.sock`.
+
+## Running example
+
+First build the example: `go build git.fd.io/govpp.git/examples/stats-api`.
+
+### Higher-level access to stats
+
+Use commands following commands to retrieve stats that are aggregated and
+processed into logical structures from [api package](../../api).
+
+- `system` to retrieve system statistics
+- `nodes` to retrieve per node statistics
+- `interfaces` to retrieve per interface statistics
+- `errors` to retrieve error statistics (you can use patterns to filter the errors)
+
+#### System stats
+
+Following command will retrieve system stats.
+```
+$ ./stats-api system
+System stats: &{VectorRate:0 InputRate:0 LastUpdate:32560 LastStatsClear:0 Heartbeat:3255}
+```
+
+#### Node stats
+
+Following command will retrieve per node stats.
+```
+$ ./stats-api nodes
+Listing node stats..
+...
+ - {NodeIndex:554 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:555 Clocks:189609 Vectors:15 Calls:15 Suspends:0}
+ - {NodeIndex:556 Clocks:2281847 Vectors:0 Calls:0 Suspends:21}
+ - {NodeIndex:557 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:558 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:559 Clocks:7094 Vectors:0 Calls:1 Suspends:1}
+ - {NodeIndex:560 Clocks:88159323916601 Vectors:0 Calls:14066116 Suspends:0}
+ - {NodeIndex:561 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:562 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:563 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:564 Clocks:447894125 Vectors:0 Calls:0 Suspends:32395}
+ - {NodeIndex:565 Clocks:1099655497824612 Vectors:0 Calls:40 Suspends:117}
+ - {NodeIndex:566 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:567 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:568 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:569 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:570 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:571 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+ - {NodeIndex:572 Clocks:0 Vectors:0 Calls:0 Suspends:0}
+Listed 573 node counters
+```
+
+#### Interface stats
+
+Following command will retrieve per interface stats.
+```
+$ ./stats-api interfaces
+Listing interface stats..
+ - {InterfaceIndex:0 RxPackets:0 RxBytes:0 RxErrors:0 TxPackets:0 TxBytes:0 TxErrors:0 RxUnicast:[0 0] RxMulticast:[0 0] RxBroadcast:[0 0] TxUnicastMiss:[0 0] TxMulticast:[0 0] TxBroadcast:[0 0] Drops:0 Punts:0 IP4:0 IP6:0 RxNoBuf:0 RxMiss:0}
+ - {InterfaceIndex:1 RxPackets:0 RxBytes:0 RxErrors:0 TxPackets:0 TxBytes:0 TxErrors:0 RxUnicast:[0 0] RxMulticast:[0 0] RxBroadcast:[0 0] TxUnicastMiss:[0 0] TxMulticast:[0 0] TxBroadcast:[0 0] Drops:5 Punts:0 IP4:0 IP6:0 RxNoBuf:0 RxMiss:0}
+ - {InterfaceIndex:2 RxPackets:0 RxBytes:0 RxErrors:0 TxPackets:0 TxBytes:0 TxErrors:0 RxUnicast:[0 0] RxMulticast:[0 0] RxBroadcast:[0 0] TxUnicastMiss:[0 0] TxMulticast:[0 0] TxBroadcast:[0 0] Drops:0 Punts:0 IP4:0 IP6:0 RxNoBuf:0 RxMiss:0}
+ - {InterfaceIndex:3 RxPackets:0 RxBytes:0 RxErrors:0 TxPackets:0 TxBytes:0 TxErrors:0 RxUnicast:[0 0] RxMulticast:[0 0] RxBroadcast:[0 0] TxUnicastMiss:[0 0] TxMulticast:[0 0] TxBroadcast:[0 0] Drops:0 Punts:0 IP4:0 IP6:0 RxNoBuf:0 RxMiss:0}
+Listed 4 interface counters
+```
+
+#### Error stats
+
+Following command will retrieve error stats.
+Use flag `-all` to include stats with zero values.
+```
+$ ./stats-api errors ip
+Listing error stats.. ip
+ - {ip4-input/ip4 spoofed local-address packet drops 15}
+Listed 1 (825) error counters
+```
+
+### Low-level access to stats
+
+Use commands `ls` and `dump` to list and dump statistics in their raw format
+from [adapter package](../../adapter).
+Optionally, patterns can be used to filter the results.
+
+#### List stats
+
+Following command will list stats matching patterns `/sys/` and `/if/`.
+```
+$ ./stats-api ls /sys/ /if/
+Listing stats.. /sys/ /if/
+ - /sys/vector_rate
+ - /sys/input_rate
+ - /sys/last_update
+ - /sys/last_stats_clear
+ - /sys/heartbeat
+ - /sys/node/clocks
+ - /sys/node/vectors
+ - /sys/node/calls
+ - /sys/node/suspends
+ - /if/drops
+ - /if/punt
+ - /if/ip4
+ - /if/ip6
+ - /if/rx-no-buf
+ - /if/rx-miss
+ - /if/rx-error
+ - /if/tx-error
+ - /if/rx
+ - /if/rx-unicast
+ - /if/rx-multicast
+ - /if/rx-broadcast
+ - /if/tx
+ - /if/tx-unicast-miss
+ - /if/tx-multicast
+ - /if/tx-broadcast
+Listed 25 stats
+```
+
+#### Dump stats
+
+Following command will dump stats with their types and actual values.
+Use flag `-all` to include stats with zero values.
+```
+$ ./stats-api dump
+Dumping stats..
+ - /sys/last_update ScalarIndex 10408
+ - /sys/heartbeat ScalarIndex 1041
+ - /err/ip4-icmp-error/unknown type ErrorIndex 5
+ - /net/route/to CombinedCounterVector [[{Packets:0 Bytes:0} {Packets:0 Bytes:0} {Packets:0 Bytes:0} {Packets:0 Bytes:0} {Packets:0 Bytes:0} {Packets:0 Bytes:0} {Packets:0 Bytes:0} {Packets:0 Bytes:0} {Packets:0 Bytes:0} {Packets:0 Bytes:0} {Packets:0 Bytes:0} {Packets:0 Bytes:0} {Packets:0 Bytes:0} {Packets:5 Bytes:420}]]
+ - /if/drops SimpleCounterVector [[0 5 5]]
+Dumped 5 (2798) stats
+```
diff --git a/examples/stats-client/stats_api.go b/examples/stats-client/stats_api.go
new file mode 100644
index 0000000..288caea
--- /dev/null
+++ b/examples/stats-client/stats_api.go
@@ -0,0 +1,260 @@
+// Copyright (c) 2018 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "strings"
+ "time"
+
+ "git.fd.io/govpp.git/adapter"
+ "git.fd.io/govpp.git/adapter/statsclient"
+ "git.fd.io/govpp.git/api"
+ "git.fd.io/govpp.git/core"
+)
+
+// ------------------------------------------------------------------
+// Example - Stats API
+// ------------------------------------------------------------------
+// The example stats_api demonstrates how to retrieve stats
+// from the VPP using the new stats API.
+// ------------------------------------------------------------------
+
+var (
+ statsSocket = flag.String("socket", statsclient.DefaultSocketName, "Path to VPP stats socket")
+ dumpAll = flag.Bool("all", false, "Dump all stats including ones with zero values")
+ pollPeriod = flag.Duration("period", time.Second*5, "Polling interval period")
+)
+
+func init() {
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, "%s: usage [ls|dump|poll|errors|interfaces|nodes|system|buffers] <patterns>...\n", os.Args[0])
+ flag.PrintDefaults()
+ os.Exit(1)
+ }
+}
+
+func main() {
+ flag.Parse()
+ skipZeros := !*dumpAll
+
+ var patterns []string
+ if flag.NArg() > 0 {
+ patterns = flag.Args()[1:]
+ }
+
+ client := statsclient.NewStatsClient(*statsSocket)
+
+ c, err := core.ConnectStats(client)
+ if err != nil {
+ log.Fatalln("Connecting failed:", err)
+ }
+ defer c.Disconnect()
+
+ switch cmd := flag.Arg(0); cmd {
+ case "system":
+ stats := new(api.SystemStats)
+ if err := c.GetSystemStats(stats); err != nil {
+ log.Fatalln("getting system stats failed:", err)
+ }
+ fmt.Printf("System stats: %+v\n", stats)
+
+ case "poll-system":
+ pollSystem(c)
+
+ case "nodes":
+ fmt.Println("Listing node stats..")
+ stats := new(api.NodeStats)
+ if err := c.GetNodeStats(stats); err != nil {
+ log.Fatalln("getting node stats failed:", err)
+ }
+
+ for _, node := range stats.Nodes {
+ if skipZeros && node.Calls == 0 && node.Suspends == 0 && node.Clocks == 0 && node.Vectors == 0 {
+ continue
+ }
+ fmt.Printf(" - %+v\n", node)
+ }
+ fmt.Printf("Listed %d node counters\n", len(stats.Nodes))
+
+ case "interfaces":
+ fmt.Println("Listing interface stats..")
+ stats := new(api.InterfaceStats)
+ if err := c.GetInterfaceStats(stats); err != nil {
+ log.Fatalln("getting interface stats failed:", err)
+ }
+ for _, iface := range stats.Interfaces {
+ fmt.Printf(" - %+v\n", iface)
+ }
+ fmt.Printf("Listed %d interface counters\n", len(stats.Interfaces))
+
+ case "poll-interfaces":
+ pollInterfaces(c)
+
+ case "errors":
+ fmt.Printf("Listing error stats.. %s\n", strings.Join(patterns, " "))
+ stats := new(api.ErrorStats)
+ if err := c.GetErrorStats(stats); err != nil {
+ log.Fatalln("getting error stats failed:", err)
+ }
+ n := 0
+ for _, counter := range stats.Errors {
+ if skipZeros && counter.Value == 0 {
+ continue
+ }
+ fmt.Printf(" - %v\n", counter)
+ n++
+ }
+ fmt.Printf("Listed %d (%d) error counters\n", n, len(stats.Errors))
+
+ case "buffers":
+ stats := new(api.BufferStats)
+ if err := c.GetBufferStats(stats); err != nil {
+ log.Fatalln("getting buffer stats failed:", err)
+ }
+ fmt.Printf("Buffer stats: %+v\n", stats)
+
+ case "dump":
+ fmt.Printf("Dumping stats.. %s\n", strings.Join(patterns, " "))
+
+ dumpStats(client, patterns, skipZeros)
+
+ case "poll":
+ fmt.Printf("Polling stats.. %s\n", strings.Join(patterns, " "))
+
+ pollStats(client, patterns, skipZeros)
+
+ case "list", "ls", "":
+ fmt.Printf("Listing stats.. %s\n", strings.Join(patterns, " "))
+
+ listStats(client, patterns)
+
+ default:
+ fmt.Printf("invalid command: %q\n", cmd)
+ }
+}
+
+func listStats(client adapter.StatsAPI, patterns []string) {
+ list, err := client.ListStats(patterns...)
+ if err != nil {
+ log.Fatalln("listing stats failed:", err)
+ }
+
+ for _, stat := range list {
+ fmt.Printf(" - %v\n", stat)
+ }
+
+ fmt.Printf("Listed %d stats\n", len(list))
+}
+
+func dumpStats(client adapter.StatsAPI, patterns []string, skipZeros bool) {
+ stats, err := client.DumpStats(patterns...)
+ if err != nil {
+ log.Fatalln("dumping stats failed:", err)
+ }
+
+ n := 0
+ for _, stat := range stats {
+ if skipZeros && (stat.Data == nil || stat.Data.IsZero()) {
+ continue
+ }
+ fmt.Printf(" - %-50s %25v %+v\n", stat.Name, stat.Type, stat.Data)
+ n++
+ }
+
+ fmt.Printf("Dumped %d (%d) stats\n", n, len(stats))
+}
+
+func pollStats(client adapter.StatsAPI, patterns []string, skipZeros bool) {
+ dir, err := client.PrepareDir(patterns...)
+ if err != nil {
+ log.Fatalln("preparing dir failed:", err)
+ }
+
+ tick := time.Tick(*pollPeriod)
+ for {
+ n := 0
+ fmt.Println(time.Now().Format(time.Stamp))
+ for _, stat := range dir.Entries {
+ if skipZeros && (stat.Data == nil || stat.Data.IsZero()) {
+ continue
+ }
+ fmt.Printf("%-50s %+v\n", stat.Name, stat.Data)
+ n++
+ }
+ fmt.Println()
+
+ select {
+ case <-tick:
+ if err := client.UpdateDir(dir); err != nil {
+ if err == adapter.ErrStatsDirStale {
+ if dir, err = client.PrepareDir(patterns...); err != nil {
+ log.Fatalln("preparing dir failed:", err)
+ }
+ continue
+ }
+ log.Fatalln("updating dir failed:", err)
+ }
+ }
+ }
+}
+
+func pollSystem(client api.StatsProvider) {
+ stats := new(api.SystemStats)
+
+ if err := client.GetSystemStats(stats); err != nil {
+ log.Fatalln("updating system stats failed:", err)
+ }
+
+ tick := time.Tick(*pollPeriod)
+ for {
+ fmt.Printf("System stats: %+v\n", stats)
+ fmt.Println()
+
+ select {
+ case <-tick:
+ if err := client.GetSystemStats(stats); err != nil {
+ log.Println("updating system stats failed:", err)
+ }
+ }
+ }
+}
+
+func pollInterfaces(client api.StatsProvider) {
+ stats := new(api.InterfaceStats)
+
+ if err := client.GetInterfaceStats(stats); err != nil {
+ log.Fatalln("updating system stats failed:", err)
+ }
+
+ tick := time.Tick(*pollPeriod)
+ for {
+ fmt.Printf("Interface stats (%d interfaces)\n", len(stats.Interfaces))
+ for i := range stats.Interfaces {
+ fmt.Printf(" - %+v\n", stats.Interfaces[i])
+ }
+ fmt.Println()
+
+ select {
+ case <-tick:
+ if err := client.GetInterfaceStats(stats); err != nil {
+ log.Println("updating system stats failed:", err)
+ }
+ }
+ }
+}