summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/api-trace/README.md17
-rw-r--r--examples/api-trace/api-trace.go348
2 files changed, 365 insertions, 0 deletions
diff --git a/examples/api-trace/README.md b/examples/api-trace/README.md
new file mode 100644
index 0000000..6cb6ce8
--- /dev/null
+++ b/examples/api-trace/README.md
@@ -0,0 +1,17 @@
+# API trace example
+
+The example demonstrates how to use GoVPP API trace functionality. Connection object `core.Connection` contains
+API tracer able to record API messages sent to and from VPP.
+
+Access to the tracer is done via `Trace()`. It allows accessing several methods to manage collected entries:
+* `Enable(<bool>)` either enables or disables the trace. Note that the trace is disabled by default and messages are not recorded while so.
+* `GetRecords() []*api.Record` provide messages collected since the plugin was enabled or cleared.
+* `GetRecordsForChannel(<channelID>) []*api.Record` provide messages collected on the given channel since the plugin was enabled or cleared.
+* `Clear()` removes recorded messages.
+
+A record is represented by `Record` type. It contains information about the message, its direction, time and channel ID. Following fields are available:
+* `Message api.Message` returns recorded entry as GoVPP Message.
+* `Timestamp time.Time` is the message timestamp.
+* `IsReceived bool` is true if the message is a reply or details message, false otherwise.
+* `ChannelID uint16` is the ID of channel processing the traced message.
+
diff --git a/examples/api-trace/api-trace.go b/examples/api-trace/api-trace.go
new file mode 100644
index 0000000..3a78c7b
--- /dev/null
+++ b/examples/api-trace/api-trace.go
@@ -0,0 +1,348 @@
+// Copyright (c) 2021 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.
+
+// api-trace is and example how to use the GoVPP API trace tool.
+
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "git.fd.io/govpp.git"
+ "git.fd.io/govpp.git/adapter/socketclient"
+ "git.fd.io/govpp.git/api"
+ interfaces "git.fd.io/govpp.git/binapi/interface"
+ "git.fd.io/govpp.git/binapi/interface_types"
+ "git.fd.io/govpp.git/binapi/ip_types"
+ "git.fd.io/govpp.git/binapi/vpe"
+ "git.fd.io/govpp.git/core"
+ "log"
+)
+
+var (
+ sockAddr = flag.String("socket", socketclient.DefaultSocketName, "Path to VPP API socket file")
+)
+
+func main() {
+ flag.Parse()
+
+ fmt.Printf("Starting api-trace tool example\n\n")
+
+ // make synchronous VPP connection
+ conn, err := govpp.Connect(*sockAddr)
+ if err != nil {
+ log.Fatalln("ERROR:", err)
+ }
+ defer conn.Disconnect()
+
+ singleChannel(conn)
+ multiChannel(conn)
+ stream(conn)
+
+ fmt.Printf("Api-trace tool example finished\n\n")
+}
+
+func singleChannel(conn *core.Connection) {
+ // create new channel and perform simple compatibility check
+ ch, err := conn.NewAPIChannel()
+ if err != nil {
+ log.Fatalln("ERROR: creating channel failed:", err)
+ }
+ defer ch.Close()
+
+ fmt.Printf("=> Example 1\n\nEnabling API trace...\n")
+ conn.Trace().Enable(true)
+
+ if err := ch.CheckCompatiblity(append(vpe.AllMessages(), interfaces.AllMessages()...)...); err != nil {
+ log.Fatal(err)
+ }
+
+ // do some API calls
+ fmt.Printf("Calling VPP API...\n")
+ retrieveVersion(ch)
+ idx := createLoopback(ch)
+ addIPAddress("10.10.0.1/24", ch, idx)
+ interfaceDump(ch)
+ deleteLoopback(ch, idx)
+ fmt.Println()
+
+ fmt.Printf("API trace (api calls: %d):\n", len(conn.Trace().GetRecords()))
+ fmt.Printf("--------------------\n")
+ for _, item := range conn.Trace().GetRecords() {
+ printTrace(item)
+ }
+ fmt.Printf("--------------------\n")
+
+ fmt.Printf("Clearing API trace...\n\n")
+ conn.Trace().Clear()
+}
+
+func multiChannel(conn *core.Connection) {
+ ch1, err := conn.NewAPIChannel()
+ if err != nil {
+ log.Fatalln("ERROR: creating channel failed:", err)
+ }
+ defer ch1.Close()
+ ch2, err := conn.NewAPIChannel()
+ if err != nil {
+ log.Fatalln("ERROR: creating channel failed:", err)
+ }
+ defer ch2.Close()
+
+ //do API calls again
+ fmt.Printf("=> Example 2\n\nCalling VPP API (multi-channel)...\n")
+ retrieveVersion(ch1)
+ idx1 := createLoopback(ch1)
+ idx2 := createLoopback(ch2)
+ addIPAddress("20.10.0.1/24", ch1, idx1)
+ addIPAddress("30.10.0.1/24", ch2, idx2)
+ interfaceDump(ch1)
+ deleteLoopback(ch2, idx1)
+ deleteLoopback(ch1, idx2)
+ fmt.Println()
+
+ chan1, ok := ch1.(*core.Channel)
+ if !ok {
+ log.Fatalln("ERROR: incorrect type of channel 1:", err)
+ }
+ chan2, ok := ch2.(*core.Channel)
+ if !ok {
+ log.Fatalln("ERROR: incorrect type of channel 2:", err)
+ }
+
+ fmt.Printf("API trace for channel 1 (api calls: %d):\n", len(conn.Trace().GetRecordsForChannel(chan1.GetID())))
+ fmt.Printf("--------------------\n")
+ for _, item := range conn.Trace().GetRecordsForChannel(chan1.GetID()) {
+ printTrace(item)
+ }
+ fmt.Printf("--------------------\n")
+ fmt.Printf("API trace for channel 2 (api calls: %d):\n", len(conn.Trace().GetRecordsForChannel(chan2.GetID())))
+ fmt.Printf("--------------------\n")
+ for _, item := range conn.Trace().GetRecordsForChannel(chan2.GetID()) {
+ printTrace(item)
+ }
+ fmt.Printf("--------------------\n")
+ fmt.Printf("cumulative API trace (api calls: %d):\n", len(conn.Trace().GetRecords()))
+ fmt.Printf("--------------------\n")
+ for _, item := range conn.Trace().GetRecords() {
+ printTrace(item)
+ }
+ fmt.Printf("--------------------\n")
+
+ fmt.Printf("Clearing API trace...\n\n")
+ conn.Trace().Clear()
+}
+
+func stream(conn *core.Connection) {
+ // create new channel and perform simple compatibility check
+ s, err := conn.NewStream(context.Background())
+ if err != nil {
+ log.Fatalln("ERROR: creating channel failed:", err)
+ }
+ defer func() {
+ if err := s.Close(); err != nil {
+ log.Fatalf("failed to close stream: %v", err)
+ }
+ }()
+
+ // do some API calls
+ fmt.Printf("=> Example 3\n\nCalling VPP API (stream)...\n")
+ invokeRetrieveVersion(conn)
+ idx := invokeCreateLoopback(conn)
+ invokeAddIPAddress("40.10.0.1/24", conn, idx)
+ invokeInterfaceDump(conn)
+ invokeDeleteLoopback(conn, idx)
+ fmt.Println()
+
+ fmt.Printf("stream API trace (api calls: %d):\n", len(conn.Trace().GetRecords()))
+ fmt.Printf("--------------------\n")
+ for _, item := range conn.Trace().GetRecords() {
+ printTrace(item)
+ }
+ fmt.Printf("--------------------\n")
+
+ fmt.Printf("Clearing API trace...\n\n")
+ conn.Trace().GetRecords()
+}
+
+func retrieveVersion(ch api.Channel) {
+ req := &vpe.ShowVersion{}
+ reply := &vpe.ShowVersionReply{}
+
+ if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+ fmt.Printf(" - retrieved VPP version: %s\n", reply.Version)
+}
+
+func invokeRetrieveVersion(c api.Connection) {
+ req := &vpe.ShowVersion{}
+ reply := &vpe.ShowVersionReply{}
+
+ if err := c.Invoke(context.Background(), req, reply); err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ }
+ fmt.Printf(" - retrieved VPP version: %s\n", reply.Version)
+}
+
+func createLoopback(ch api.Channel) interface_types.InterfaceIndex {
+ req := &interfaces.CreateLoopback{}
+ reply := &interfaces.CreateLoopbackReply{}
+
+ if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return 0
+ }
+ fmt.Printf(" - created loopback with index: %d\n", reply.SwIfIndex)
+ return reply.SwIfIndex
+}
+
+func invokeCreateLoopback(c api.Connection) interface_types.InterfaceIndex {
+ req := &interfaces.CreateLoopback{}
+ reply := &interfaces.CreateLoopbackReply{}
+
+ if err := c.Invoke(context.Background(), req, reply); err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return 0
+ }
+ fmt.Printf(" - created loopback with index: %d\n", reply.SwIfIndex)
+ return reply.SwIfIndex
+}
+
+func deleteLoopback(ch api.Channel, index interface_types.InterfaceIndex) {
+ req := &interfaces.DeleteLoopback{
+ SwIfIndex: index,
+ }
+ reply := &interfaces.DeleteLoopbackReply{}
+
+ if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+ fmt.Printf(" - deleted loopback with index: %d\n", index)
+}
+
+func invokeDeleteLoopback(c api.Connection, index interface_types.InterfaceIndex) {
+ req := &interfaces.DeleteLoopback{
+ SwIfIndex: index,
+ }
+ reply := &interfaces.DeleteLoopbackReply{}
+
+ if err := c.Invoke(context.Background(), req, reply); err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+ fmt.Printf(" - deleted loopback with index: %d\n", index)
+}
+
+func addIPAddress(addr string, ch api.Channel, index interface_types.InterfaceIndex) {
+ ipAddr, err := ip_types.ParsePrefix(addr)
+ if err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+
+ req := &interfaces.SwInterfaceAddDelAddress{
+ SwIfIndex: index,
+ IsAdd: true,
+ Prefix: ip_types.AddressWithPrefix(ipAddr),
+ }
+ reply := &interfaces.SwInterfaceAddDelAddressReply{}
+
+ if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+ fmt.Printf(" - IP address %s added to interface with index %d\n", addr, index)
+}
+
+func invokeAddIPAddress(addr string, c api.Connection, index interface_types.InterfaceIndex) {
+ ipAddr, err := ip_types.ParsePrefix(addr)
+ if err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+
+ req := &interfaces.SwInterfaceAddDelAddress{
+ SwIfIndex: index,
+ IsAdd: true,
+ Prefix: ip_types.AddressWithPrefix(ipAddr),
+ }
+ reply := &interfaces.SwInterfaceAddDelAddressReply{}
+
+ if err := c.Invoke(context.Background(), req, reply); err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+ fmt.Printf(" - IP address %s added to interface with index %d\n", addr, index)
+}
+
+func interfaceDump(ch api.Channel) {
+ reqCtx := ch.SendMultiRequest(&interfaces.SwInterfaceDump{
+ SwIfIndex: ^interface_types.InterfaceIndex(0),
+ })
+ for {
+ msg := &interfaces.SwInterfaceDetails{}
+ stop, err := reqCtx.ReceiveReply(msg)
+ if stop {
+ break
+ }
+ if err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+ fmt.Printf(" - retrieved interface: %v (idx: %d)\n", msg.InterfaceName, msg.SwIfIndex)
+ }
+}
+
+func invokeInterfaceDump(c api.Connection) {
+ s, err := c.NewStream(context.Background())
+ if err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+ if err := s.SendMsg(&interfaces.SwInterfaceDump{}); err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+ if err := s.SendMsg(&vpe.ControlPing{}); err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+ for {
+ reply, err := s.RecvMsg()
+ if err != nil {
+ fmt.Printf("ERROR: %v\n", err)
+ return
+ }
+ switch msg := reply.(type) {
+ case *interfaces.SwInterfaceDetails:
+ fmt.Printf(" - retrieved interface: %v (idx: %d)\n", msg.InterfaceName, msg.SwIfIndex)
+ case *vpe.ControlPingReply:
+ return
+ }
+ }
+}
+
+func printTrace(item *api.Record) {
+ h, m, s := item.Timestamp.Clock()
+ reply := ""
+ if item.IsReceived {
+ reply = "(reply)"
+ }
+ fmt.Printf("%dh:%dm:%ds:%dns %s %s\n", h, m, s,
+ item.Timestamp.Nanosecond(), item.Message.GetMessageName(), reply)
+}