aboutsummaryrefslogtreecommitdiffstats
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.go349
-rw-r--r--examples/binapi-types/binapi_types.go54
-rw-r--r--examples/multi-vpp/README.md52
-rw-r--r--examples/multi-vpp/multi_vpp.go331
-rw-r--r--examples/perf-bench/perf-bench.go26
-rw-r--r--examples/rpc-service/rpc_service.go10
-rw-r--r--examples/simple-client/simple_client.go145
-rw-r--r--examples/stats-client/README.md2
-rw-r--r--examples/stats-client/stats_api.go133
-rw-r--r--examples/stream-client/stream_client.go303
11 files changed, 1166 insertions, 256 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..3654de5
--- /dev/null
+++ b/examples/api-trace/api-trace.go
@@ -0,0 +1,349 @@
+// 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"
+ "go.fd.io/govpp"
+ "go.fd.io/govpp/adapter/socketclient"
+ "go.fd.io/govpp/api"
+ interfaces "go.fd.io/govpp/binapi/interface"
+ "go.fd.io/govpp/binapi/interface_types"
+ "go.fd.io/govpp/binapi/ip_types"
+ "go.fd.io/govpp/binapi/memclnt"
+ "go.fd.io/govpp/binapi/vpe"
+ "go.fd.io/govpp/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(&memclnt.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 *memclnt.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)
+}
diff --git a/examples/binapi-types/binapi_types.go b/examples/binapi-types/binapi_types.go
index 849ad1b..75bf9b4 100644
--- a/examples/binapi-types/binapi_types.go
+++ b/examples/binapi-types/binapi_types.go
@@ -12,42 +12,49 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// union-example is an example to show how to use unions in VPP binary API.
+// binapi-types is an example showing how to use and convert data with
+// helper methods from *-types packages in VPP binary API.
package main
import (
"fmt"
"log"
+ "time"
- "git.fd.io/govpp.git/binapi/ethernet_types"
- "git.fd.io/govpp.git/binapi/ip"
- "git.fd.io/govpp.git/binapi/ip_types"
- "git.fd.io/govpp.git/codec"
+ "go.fd.io/govpp/binapi/ethernet_types"
+ "go.fd.io/govpp/binapi/ip"
+ "go.fd.io/govpp/binapi/ip_types"
+ "go.fd.io/govpp/binapi/vpe_types"
+ "go.fd.io/govpp/codec"
)
func init() {
- log.SetFlags(0)
}
func main() {
- addressUnionExample()
- ipAddressExample()
+ log.SetFlags(0)
+
+ usageUnion()
+ usageAddress()
- // convert IP from string form into Address type containing union
- convertIP("10.10.1.1")
- convertIP("ff80::1")
+ // convert IP address in string form into ip_types.Address
+ convertIPAddress("10.10.1.1")
+ convertIPAddress("ff80::1")
- // convert IP from string form into Prefix type
+ // convert IP address / CIDR in string form into ip_types.Prefix
convertIPPrefix("20.10.1.1/24")
convertIPPrefix("21.10.1.1")
convertIPPrefix("ff90::1/64")
convertIPPrefix("ff91::1")
- // convert MAC address from string into MacAddress
+ // convert MAC address in string form into ethernet_types.MacAddress
convertToMacAddress("00:10:ab:4f:00:01")
+
+ // convert time.Time into vpe_types.Timestamp
+ convertToTimestamp(time.Now())
}
-func addressUnionExample() {
+func usageUnion() {
var union ip_types.AddressUnion
// initialize union using constructors
@@ -63,21 +70,17 @@ func addressUnionExample() {
union.SetIP6(ip6)
}
-func ipAddressExample() {
+func usageAddress() {
// parse string into IP address
- addrIP4, err := ip_types.ParseAddress("192.168.1.10")
+ addr, err := ip_types.ParseAddress("192.168.1.10")
if err != nil {
panic(err)
}
- /*addrIP6, err := ip_types.ParseAddress("ff:2::2")
- if err != nil {
- panic(err)
- }*/
var msg = ip.IPPuntRedirect{
IsAdd: true,
Punt: ip.PuntRedirect{
- Nh: addrIP4,
+ Nh: addr,
},
}
@@ -103,7 +106,7 @@ func ipAddressExample() {
}
}
-func convertIP(ip string) {
+func convertIPAddress(ip string) {
addr, err := ip_types.ParseAddress(ip)
if err != nil {
log.Printf("error converting IP to Address: %v", err)
@@ -135,3 +138,10 @@ func convertToMacAddress(mac string) {
fmt.Printf("MacAddress converted back to string %#v to: %s\n", parsedMac, parsedMac)
}
+
+func convertToTimestamp(t time.Time) {
+ timestamp := vpe_types.NewTimestamp(t)
+ fmt.Printf("converted time %v to: %#v\n", t, timestamp)
+
+ fmt.Printf("Timestamp converted back to string %#v to: %s\n", timestamp, timestamp)
+}
diff --git a/examples/multi-vpp/README.md b/examples/multi-vpp/README.md
new file mode 100644
index 0000000..2999ae9
--- /dev/null
+++ b/examples/multi-vpp/README.md
@@ -0,0 +1,52 @@
+# Multi-VPP example
+
+This example shows how to use GoVPP client to connect, configure and read stats from multiple VPPs simultaneously.
+
+# Requirements
+
+* VPP 19.08 or newer (required for stats client)
+* VPP stats enabled
+
+The example requires two running VPP instances. VPPs can be simply started in the same machine with different startup configs. Note that the default path to binary API and stats sockets are `/run/vpp/api.sock` or `/run/vpp/stats.sock` respectively. The example always uses the default path if none is set. It means that at least one VPP must have the following fields redefined:
+
+```
+socksvr {
+ socket-name /run/custom-vpp-path/api.sock
+}
+
+statseg {
+ socket-name /run/custom-vpp-path/stats.sock
+}
+```
+
+And the custom path must be provided to the example. Four flags are available:
+```
+-api-sock-1 string - Path to binary API socket file of the VPP1 (default "/run/vpp/api.sock")
+-api-sock-2 string - Path to binary API socket file of the VPP2 (default "/run/vpp/api.sock")
+-stats-sock-1 string - Path to stats socket file of the VPP1 (default "/run/vpp/stats.sock")
+-stats-sock-2 string - Path to stats socket file of the VPP2 (default "/run/vpp/stats.sock")
+```
+Let's say the VPP1 uses the default config, and the config above belongs to the VPP2. In that case, use the following command:
+```
+sudo ./multi-vpp -api-sock-2=/run/custom-vpp-path/api.sock -stats-sock-2=/run/custom-vpp-path/stats.sock
+```
+
+# Running the example
+
+The example consists of the following steps:
+* connects to both VPPs binary API socket and stats socket
+* configures example interfaces with IP addresses
+* dumps interface data via the binary API
+* dumps interface data via socket client
+* in case there are no errors, cleans up VPPs in order to be able running the example in a loop
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/multi-vpp/multi_vpp.go b/examples/multi-vpp/multi_vpp.go
index 8714c9a..10f2e70 100644
--- a/examples/multi-vpp/multi_vpp.go
+++ b/examples/multi-vpp/multi_vpp.go
@@ -20,102 +20,172 @@ import (
"fmt"
"log"
"os"
-
- "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"
- "git.fd.io/govpp.git/binapi/ip_types"
- "git.fd.io/govpp.git/binapi/vpe"
- "git.fd.io/govpp.git/core"
+ "strings"
+
+ "go.fd.io/govpp"
+ "go.fd.io/govpp/adapter/socketclient"
+ "go.fd.io/govpp/adapter/statsclient"
+ "go.fd.io/govpp/api"
+ interfaces "go.fd.io/govpp/binapi/interface"
+ "go.fd.io/govpp/binapi/interface_types"
+ "go.fd.io/govpp/binapi/ip"
+ "go.fd.io/govpp/binapi/ip_types"
+ "go.fd.io/govpp/binapi/vpe"
+ "go.fd.io/govpp/core"
)
var (
- sockAddrVpp1 = flag.String("sock1", socketclient.DefaultSocketName, "Path to binary API socket file of the first VPP instance")
- sockAddrVpp2 = flag.String("sock2", socketclient.DefaultSocketName, "Path to binary API socket file of the second VPP instance")
+ binapiSockAddrVpp1 = flag.String("api-sock-1", socketclient.DefaultSocketName, "Path to binary API socket file of the VPP1")
+ statsSockAddrVpp1 = flag.String("stats-sock-1", statsclient.DefaultSocketName, "Path to stats socket file of the VPP1")
+ binapiSockAddrVpp2 = flag.String("api-sock-2", socketclient.DefaultSocketName, "Path to binary API socket file of the VPP2")
+ statsSockAddrVpp2 = flag.String("stats-sock-2", statsclient.DefaultSocketName, "Path to stats socket file of the VPP2")
)
+var errors []error
+
func main() {
flag.Parse()
fmt.Println("Starting multi-vpp example")
- // since both of them default to the same value
- if *sockAddrVpp1 == *sockAddrVpp2 {
- log.Fatalln("ERROR: identical VPP sockets defined, set at least one of them to non-default path")
- }
+ defer func() {
+ if len(errors) > 0 {
+ logInfo("Finished with %d errors\n", len(errors))
+ os.Exit(1)
+ } else {
+ logInfo("Finished successfully\n")
+ }
+ }()
- // connect VPP1
- conn1, err := connectToVPP(*sockAddrVpp1, 1)
- if err != nil {
- log.Fatalf("ERROR: connecting VPP failed (socket %s): %v\n", *sockAddrVpp1, err)
+ // since sockets default to the same value
+ if *binapiSockAddrVpp1 == *binapiSockAddrVpp2 {
+ log.Fatalln("ERROR: identical VPP binapi sockets defined, set at least one of them to a non-default path")
}
- defer conn1.Disconnect()
- ch1, err := getAPIChannel(conn1)
- if err != nil {
- log.Fatalf("ERROR: creating channel failed (socket: %s): %v\n", *sockAddrVpp1, err)
+ if *statsSockAddrVpp1 == *statsSockAddrVpp2 {
+ log.Fatalln("ERROR: identical VPP stats sockets defined, set at least one of them to a non-default path")
}
- defer ch1.Close()
+ var name1, name2 = "vpp1", "vpp2"
+ ch1, statsConn1, disconnect1 := connectVPP(name1, *binapiSockAddrVpp1, *statsSockAddrVpp1)
+ defer disconnect1()
+
+ ch2, statsConn2, disconnect2 := connectVPP(name2, *binapiSockAddrVpp2, *statsSockAddrVpp2)
+ defer disconnect2()
+
+ fmt.Println()
+
+ // retrieve VPP1 version
+ logHeader("Retrieving %s version", name1)
+ getVppVersion(ch1, name1)
+
+ // retrieve VPP2 version
+ logHeader("Retrieving %s version", name2)
+ getVppVersion(ch1, name2)
+
+ // configure VPP1
+ logHeader("Configuring %s", name1)
+ ifIdx1 := createLoopback(ch1, name1)
+ addIPsToInterface(ch1, ifIdx1, []string{"10.10.0.1/24", "15.10.0.1/24"})
+
+ // configure VPP2
+ logHeader("Configuring %s", name2)
+ ifIdx2 := createLoopback(ch2, name2)
+ addIPsToInterface(ch2, ifIdx2, []string{"20.10.0.1/24", "25.10.0.1/24"})
+
+ // retrieve configuration from VPPs
+ retrieveIPAddresses(ch1, name1, ifIdx1)
+ retrieveIPAddresses(ch2, name2, ifIdx2)
+
+ // retrieve stats from VPPs
+ retrieveStats(statsConn1, name1)
+ retrieveStats(statsConn2, name2)
+
+ // cleanup
+ logHeader("Cleaning up %s", name1)
+ deleteIPsToInterface(ch1, ifIdx1, []string{"10.10.0.1/24", "15.10.0.1/24"})
+ deleteLoopback(ch1, ifIdx1)
+ logHeader("Cleaning up %s", name2)
+ deleteIPsToInterface(ch2, ifIdx2, []string{"20.10.0.1/24", "25.10.0.1/24"})
+ deleteLoopback(ch2, ifIdx2)
+}
+
+func connectVPP(name, binapiSocket, statsSocket string) (api.Channel, api.StatsProvider, func()) {
+ fmt.Println()
+ logHeader("Connecting to %s", name)
- // connect VPP2
- conn2, err := connectToVPP(*sockAddrVpp2, 2)
+ // connect VPP1 to the binapi socket
+ ch, disconnectBinapi, err := connectBinapi(binapiSocket, 1)
if err != nil {
- log.Fatalf("ERROR: connecting VPP failed (socket %s): %v\n", *sockAddrVpp2, err)
+ log.Fatalf("ERROR: connecting VPP binapi failed (socket %s): %v\n", binapiSocket, err)
}
- defer conn2.Disconnect()
- ch2, err := getAPIChannel(conn2)
+
+ // connect VPP1 to the stats socket
+ statsConn, disconnectStats, err := connectStats(name, statsSocket)
if err != nil {
- log.Fatalf("ERROR: creating channel failed (socket: %s): %v\n", *sockAddrVpp2, err)
+ disconnectBinapi()
+ log.Fatalf("ERROR: connecting VPP stats failed (socket %s): %v\n", statsSocket, err)
}
- defer ch2.Close()
-
- // configure VPPs
- ifIdx1 := createLoopback(ch1)
- addIPToInterface(ch1, ifIdx1, "10.10.0.1/24")
- ifIdx2 := createLoopback(ch2)
- addIPToInterface(ch2, ifIdx2, "20.10.0.1/24")
- // retrieve configuration from the VPPs
- retrieveIPAddresses(ch1, ifIdx1)
- retrieveIPAddresses(ch2, ifIdx2)
+ logInfo("OK\n")
- if len(Errors) > 0 {
- fmt.Printf("finished with %d errors\n", len(Errors))
- os.Exit(1)
- } else {
- fmt.Println("finished successfully")
+ return ch, statsConn, func() {
+ disconnectStats()
+ disconnectBinapi()
+ logInfo("VPP %s disconnected\n", name)
}
}
-func connectToVPP(socket string, attempts int) (*core.Connection, error) {
- connection, event, err := govpp.AsyncConnect(socket, attempts, core.DefaultReconnectInterval)
+// connectBinapi connects to the binary API socket and returns a communication channel
+func connectBinapi(socket string, attempts int) (api.Channel, func(), error) {
+ logInfo("Attaching to the binapi socket %s\n", socket)
+ conn, event, err := govpp.AsyncConnect(socket, attempts, core.DefaultReconnectInterval)
if err != nil {
- return nil, err
+ return nil, nil, err
}
-
- // handle connection event
select {
case e := <-event:
if e.State != core.Connected {
- return nil, err
+ return nil, nil, err
+ }
+ }
+ ch, err := getAPIChannel(conn)
+ if err != nil {
+ return nil, nil, err
+ }
+ disconnect := func() {
+ if ch != nil {
+ ch.Close()
+ }
+ if conn != nil {
+ conn.Disconnect()
}
}
- return connection, nil
+ return ch, disconnect, nil
}
-func getAPIChannel(conn *core.Connection) (api.Channel, error) {
- ch, err := conn.NewAPIChannel()
+// connectStats connects to the stats socket and returns a stats provider
+func connectStats(name, socket string) (api.StatsProvider, func(), error) {
+ logInfo("Attaching to the stats socket %s\n", socket)
+ sc := statsclient.NewStatsClient(socket)
+ conn, err := core.ConnectStats(sc)
if err != nil {
- return nil, err
+ return nil, nil, err
+ }
+ disconnect := func() {
+ if err := sc.Disconnect(); err != nil {
+ logError(err, "failed to disconnect "+name+" stats socket")
+ }
}
+ return conn, disconnect, nil
+}
+// getAPIChannel creates new API channel and verifies its compatibility
+func getAPIChannel(c api.ChannelProvider) (api.Channel, error) {
+ ch, err := c.NewAPIChannel()
+ if err != nil {
+ return nil, err
+ }
if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
return nil, err
}
-
- getVppVersion(ch)
-
if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
return nil, err
}
@@ -123,8 +193,8 @@ func getAPIChannel(conn *core.Connection) (api.Channel, error) {
}
// getVppVersion returns VPP version (simple API usage)
-func getVppVersion(ch api.Channel) {
- fmt.Println("Retrieving version")
+func getVppVersion(ch api.Channel, name string) {
+ logInfo("Retrieving version of %s ..\n", name)
req := &vpe.ShowVersion{}
reply := &vpe.ShowVersionReply{}
@@ -133,23 +203,13 @@ func getVppVersion(ch api.Channel) {
logError(err, "retrieving version")
return
}
- fmt.Printf("reply: %+v\n", reply)
-
- fmt.Printf("VPP version: %q\n", reply.Version)
- fmt.Println("OK")
+ logInfo("Retrieved version is %q\n", reply.Version)
fmt.Println()
}
-var Errors []error
-
-func logError(err error, msg string) {
- fmt.Printf("ERROR: %s: %v\n", msg, err)
- Errors = append(Errors, err)
-}
-
// createLoopback sends request to create a loopback interface
-func createLoopback(ch api.Channel) interface_types.InterfaceIndex {
- fmt.Println("Adding loopback interface")
+func createLoopback(ch api.Channel, name string) interface_types.InterfaceIndex {
+ logInfo("Adding loopback interface ..\n")
req := &interfaces.CreateLoopback{}
reply := &interfaces.CreateLoopbackReply{}
@@ -158,49 +218,85 @@ func createLoopback(ch api.Channel) interface_types.InterfaceIndex {
logError(err, "adding loopback interface")
return 0
}
- fmt.Printf("reply: %+v\n", reply)
-
- fmt.Printf("interface index: %v\n", reply.SwIfIndex)
- fmt.Println("OK")
- fmt.Println()
+ logInfo("Interface index %d added to %s\n", reply.SwIfIndex, name)
return reply.SwIfIndex
}
-// addIPToInterface sends request to add an IP address to an interface.
-func addIPToInterface(ch api.Channel, index interface_types.InterfaceIndex, ip string) {
- fmt.Printf("Setting up IP address to the interface with index %d\n", index)
- prefix, err := ip_types.ParsePrefix(ip)
- if err != nil {
- logError(err, "attempt to add invalid IP address")
- return
+// deleteLoopback removes created loopback interface
+func deleteLoopback(ch api.Channel, ifIdx interface_types.InterfaceIndex) {
+ logInfo("Removing loopback interface ..\n")
+ req := &interfaces.DeleteLoopback{
+ SwIfIndex: ifIdx,
}
+ reply := &interfaces.DeleteLoopbackReply{}
- req := &interfaces.SwInterfaceAddDelAddress{
- SwIfIndex: index,
- IsAdd: true,
- Prefix: ip_types.AddressWithPrefix(prefix),
+ if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
+ logError(err, "removing loopback interface")
}
- reply := &interfaces.SwInterfaceAddDelAddressReply{}
+ logInfo("OK\n")
+ fmt.Println()
+}
- if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
- logError(err, "adding IP address to interface")
- return
+// addIPsToInterface sends request to add IP addresses to an interface.
+func addIPsToInterface(ch api.Channel, index interface_types.InterfaceIndex, ips []string) {
+ for _, ipAddr := range ips {
+ logInfo("Adding IP address %s\n", ipAddr)
+ prefix, err := ip_types.ParsePrefix(ipAddr)
+ if err != nil {
+ logError(err, "attempt to add invalid IP address")
+ return
+ }
+
+ req := &interfaces.SwInterfaceAddDelAddress{
+ SwIfIndex: index,
+ IsAdd: true,
+ Prefix: ip_types.AddressWithPrefix(prefix),
+ }
+ reply := &interfaces.SwInterfaceAddDelAddressReply{}
+
+ if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
+ logError(err, "adding IP address to interface")
+ return
+ }
}
- fmt.Printf("reply: %+v\n", reply)
- fmt.Println("OK")
+ logInfo("OK\n")
fmt.Println()
}
-func retrieveIPAddresses(ch api.Channel, index interface_types.InterfaceIndex) {
- fmt.Printf("Retrieving IP addresses for interface index %d\n", index)
+// deleteIPsToInterface sends request to remove IP addresses from an interface.
+func deleteIPsToInterface(ch api.Channel, index interface_types.InterfaceIndex, ips []string) {
+ for _, ipAddr := range ips {
+ logInfo("Removing IP address %s\n", ipAddr)
+ prefix, err := ip_types.ParsePrefix(ipAddr)
+ if err != nil {
+ logError(err, "attempt to remove invalid IP address")
+ return
+ }
+
+ req := &interfaces.SwInterfaceAddDelAddress{
+ SwIfIndex: index,
+ Prefix: ip_types.AddressWithPrefix(prefix),
+ }
+ reply := &interfaces.SwInterfaceAddDelAddressReply{}
+ if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
+ logError(err, "removing IP address to interface")
+ return
+ }
+ }
+}
+
+// retrieveIPAddresses reads IP address from the interface
+func retrieveIPAddresses(ch api.Channel, name string, index interface_types.InterfaceIndex) {
+ logHeader("Retrieving interface data from %s", name)
req := &ip.IPAddressDump{
SwIfIndex: index,
}
reqCtx := ch.SendMultiRequest(req)
+ logInfo("Dump IP addresses for interface index %d ..\n", index)
for {
msg := &ip.IPAddressDetails{}
stop, err := reqCtx.ReceiveReply(msg)
@@ -212,9 +308,44 @@ func retrieveIPAddresses(ch api.Channel, index interface_types.InterfaceIndex) {
break
}
prefix := ip_types.Prefix(msg.Prefix)
- fmt.Printf(" - ip address: %v\n", prefix)
+ logInfo(" - ip address: %v\n", prefix)
}
- fmt.Println("OK")
+ logInfo("OK\n")
fmt.Println()
}
+
+// retrieveStats reads interface stats
+func retrieveStats(s api.StatsProvider, name string) {
+ logHeader("Retrieving interface stats from %s", name)
+ ifStats := &api.InterfaceStats{}
+ err := s.GetInterfaceStats(ifStats)
+ if err != nil {
+ logError(err, "dumping interface stats")
+ return
+ }
+ logInfo("Dump interface stats ..\n")
+ for _, ifStats := range ifStats.Interfaces {
+ logInfo(" - %+v\n", ifStats)
+ }
+
+ logInfo("OK\n")
+ fmt.Println()
+}
+
+// logHeader prints underlined message (for better output segmentation)
+func logHeader(format string, a ...interface{}) {
+ n, _ := fmt.Printf(format+"\n", a...)
+ fmt.Println(strings.Repeat("-", n-1))
+}
+
+// logInfo prints info message
+func logInfo(format string, a ...interface{}) {
+ fmt.Printf(format, a...)
+}
+
+// logError prints error message
+func logError(err error, msg string) {
+ fmt.Printf("[ERROR]: %s: %v\n", msg, err)
+ errors = append(errors, err)
+}
diff --git a/examples/perf-bench/perf-bench.go b/examples/perf-bench/perf-bench.go
index 6472068..6e25185 100644
--- a/examples/perf-bench/perf-bench.go
+++ b/examples/perf-bench/perf-bench.go
@@ -27,11 +27,11 @@ import (
"github.com/pkg/profile"
"github.com/sirupsen/logrus"
- "git.fd.io/govpp.git/adapter/socketclient"
- "git.fd.io/govpp.git/adapter/statsclient"
- "git.fd.io/govpp.git/api"
- "git.fd.io/govpp.git/binapi/vpe"
- "git.fd.io/govpp.git/core"
+ "go.fd.io/govpp/adapter/socketclient"
+ "go.fd.io/govpp/adapter/statsclient"
+ "go.fd.io/govpp/api"
+ "go.fd.io/govpp/binapi/memclnt"
+ "go.fd.io/govpp/core"
)
const (
@@ -117,8 +117,8 @@ func syncTest(ch api.Channel, cnt int) {
fmt.Printf("Running synchronous perf test with %d requests...\n", cnt)
for i := 0; i < cnt; i++ {
- req := &vpe.ControlPing{}
- reply := &vpe.ControlPingReply{}
+ req := &memclnt.ControlPing{}
+ reply := &memclnt.ControlPingReply{}
if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
log.Fatalln("Error in reply:", err)
@@ -134,12 +134,12 @@ func syncTest2(conn api.Connection, cnt int) {
log.Fatalln("Error NewStream:", err)
}
for i := 0; i < cnt; i++ {
- if err := stream.SendMsg(&vpe.ControlPing{}); err != nil {
+ if err := stream.SendMsg(&memclnt.ControlPing{}); err != nil {
log.Fatalln("Error SendMsg:", err)
}
if msg, err := stream.RecvMsg(); err != nil {
log.Fatalln("Error RecvMsg:", err)
- } else if _, ok := msg.(*vpe.ControlPingReply); ok {
+ } else if _, ok := msg.(*memclnt.ControlPingReply); ok {
// ok
} else {
log.Fatalf("unexpected reply: %v", msg.GetMessageName())
@@ -154,14 +154,14 @@ func asyncTest(ch api.Channel, cnt int) {
go func() {
for i := 0; i < cnt; i++ {
- ctxChan <- ch.SendRequest(&vpe.ControlPing{})
+ ctxChan <- ch.SendRequest(&memclnt.ControlPing{})
}
close(ctxChan)
fmt.Printf("Sending asynchronous requests finished\n")
}()
for ctx := range ctxChan {
- reply := &vpe.ControlPingReply{}
+ reply := &memclnt.ControlPingReply{}
if err := ctx.ReceiveReply(reply); err != nil {
log.Fatalln("Error in reply:", err)
}
@@ -179,7 +179,7 @@ func asyncTest2(conn api.Connection, cnt int) {
if err != nil {
log.Fatalln("Error NewStream:", err)
}
- if err := stream.SendMsg(&vpe.ControlPing{}); err != nil {
+ if err := stream.SendMsg(&memclnt.ControlPing{}); err != nil {
log.Fatalln("Error SendMsg:", err)
}
ctxChan <- stream
@@ -191,7 +191,7 @@ func asyncTest2(conn api.Connection, cnt int) {
for ctx := range ctxChan {
if msg, err := ctx.RecvMsg(); err != nil {
log.Fatalln("Error RecvMsg:", err)
- } else if _, ok := msg.(*vpe.ControlPingReply); ok {
+ } else if _, ok := msg.(*memclnt.ControlPingReply); ok {
// ok
} else {
log.Fatalf("unexpected reply: %v", msg.GetMessageName())
diff --git a/examples/rpc-service/rpc_service.go b/examples/rpc-service/rpc_service.go
index e20e5c0..71dadbf 100644
--- a/examples/rpc-service/rpc_service.go
+++ b/examples/rpc-service/rpc_service.go
@@ -24,11 +24,11 @@ import (
"log"
"strings"
- "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/vpe"
+ "go.fd.io/govpp"
+ "go.fd.io/govpp/adapter/socketclient"
+ "go.fd.io/govpp/api"
+ interfaces "go.fd.io/govpp/binapi/interface"
+ "go.fd.io/govpp/binapi/vpe"
)
var (
diff --git a/examples/simple-client/simple_client.go b/examples/simple-client/simple_client.go
index d823273..fea3e43 100644
--- a/examples/simple-client/simple_client.go
+++ b/examples/simple-client/simple_client.go
@@ -17,23 +17,21 @@
package main
import (
- "context"
"encoding/json"
"flag"
"fmt"
"log"
"os"
- "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"
- "git.fd.io/govpp.git/binapi/ip_types"
- "git.fd.io/govpp.git/binapi/mactime"
- "git.fd.io/govpp.git/binapi/vpe"
- "git.fd.io/govpp.git/core"
+ "go.fd.io/govpp"
+ "go.fd.io/govpp/adapter/socketclient"
+ "go.fd.io/govpp/api"
+ interfaces "go.fd.io/govpp/binapi/interface"
+ "go.fd.io/govpp/binapi/interface_types"
+ "go.fd.io/govpp/binapi/ip"
+ "go.fd.io/govpp/binapi/ip_types"
+ "go.fd.io/govpp/binapi/vpe"
+ "go.fd.io/govpp/core"
)
var (
@@ -44,9 +42,10 @@ func main() {
flag.Parse()
fmt.Println("Starting simple client example")
+ fmt.Println()
// connect to VPP asynchronously
- conn, conev, err := govpp.AsyncConnect(*sockAddr, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
+ conn, connEv, err := govpp.AsyncConnect(*sockAddr, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
if err != nil {
log.Fatalln("ERROR:", err)
}
@@ -54,56 +53,47 @@ func main() {
// wait for Connected event
select {
- case e := <-conev:
+ case e := <-connEv:
if e.State != core.Connected {
log.Fatalln("ERROR: connecting to VPP failed:", e.Error)
}
}
- // create an API channel that will be used in the examples
+ // check compatibility of used messages
ch, err := conn.NewAPIChannel()
if err != nil {
log.Fatalln("ERROR: creating channel failed:", err)
}
defer ch.Close()
-
if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
log.Fatal(err)
}
-
- vppVersion(ch)
-
if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
log.Fatal(err)
}
+ // process errors encountered during the example
+ defer func() {
+ if len(errors) > 0 {
+ fmt.Printf("finished with %d errors\n", len(errors))
+ os.Exit(1)
+ } else {
+ fmt.Println("finished successfully")
+ }
+ }()
+
+ // use request/reply (channel API)
+ getVppVersion(ch)
+ getSystemTime(ch)
idx := createLoopback(ch)
interfaceDump(ch)
-
addIPAddress(ch, idx)
ipAddressDump(ch, idx)
interfaceNotifications(ch, idx)
-
- mactimeDump(conn)
-
- if len(Errors) > 0 {
- fmt.Printf("finished with %d errors\n", len(Errors))
- os.Exit(1)
- } else {
- fmt.Println("finished successfully")
- }
-}
-
-var Errors []error
-
-func logError(err error, msg string) {
- fmt.Printf("ERROR: %s: %v\n", msg, err)
- Errors = append(Errors, err)
}
-// vppVersion is the simplest API example - it retrieves VPP version.
-func vppVersion(ch api.Channel) {
- fmt.Println("Retrieving version")
+func getVppVersion(ch api.Channel) {
+ fmt.Println("Retrieving version..")
req := &vpe.ShowVersion{}
reply := &vpe.ShowVersionReply{}
@@ -112,16 +102,30 @@ func vppVersion(ch api.Channel) {
logError(err, "retrieving version")
return
}
- fmt.Printf("reply: %+v\n", reply)
fmt.Printf("VPP version: %q\n", reply.Version)
fmt.Println("OK")
fmt.Println()
}
-// createLoopback sends request to create loopback interface.
+func getSystemTime(ch api.Channel) {
+ fmt.Println("Retrieving system time..")
+
+ req := &vpe.ShowVpeSystemTime{}
+ reply := &vpe.ShowVpeSystemTimeReply{}
+
+ if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
+ logError(err, "retrieving system time")
+ return
+ }
+
+ fmt.Printf("system time: %v\n", reply.VpeSystemTime)
+ fmt.Println("OK")
+ fmt.Println()
+}
+
func createLoopback(ch api.Channel) interface_types.InterfaceIndex {
- fmt.Println("Creating loopback interface")
+ fmt.Println("Creating loopback interface..")
req := &interfaces.CreateLoopback{}
reply := &interfaces.CreateLoopbackReply{}
@@ -130,7 +134,6 @@ func createLoopback(ch api.Channel) interface_types.InterfaceIndex {
logError(err, "creating loopback interface")
return 0
}
- fmt.Printf("reply: %+v\n", reply)
fmt.Printf("interface index: %v\n", reply.SwIfIndex)
fmt.Println("OK")
@@ -139,9 +142,8 @@ func createLoopback(ch api.Channel) interface_types.InterfaceIndex {
return reply.SwIfIndex
}
-// interfaceDump shows an example of multipart request (multiple replies are expected).
func interfaceDump(ch api.Channel) {
- fmt.Println("Dumping interfaces")
+ fmt.Println("Dumping interfaces..")
n := 0
reqCtx := ch.SendMultiRequest(&interfaces.SwInterfaceDump{
@@ -166,9 +168,8 @@ func interfaceDump(ch api.Channel) {
fmt.Println()
}
-// addIPAddress sends request to add IP address to interface.
func addIPAddress(ch api.Channel, index interface_types.InterfaceIndex) {
- fmt.Printf("Adding IP address to interface to interface index %d\n", index)
+ fmt.Printf("Adding IP address to interface index %d\n", index)
req := &interfaces.SwInterfaceAddDelAddress{
SwIfIndex: index,
@@ -188,14 +189,13 @@ func addIPAddress(ch api.Channel, index interface_types.InterfaceIndex) {
logError(err, "adding IP address to interface")
return
}
- fmt.Printf("reply: %+v\n", reply)
fmt.Println("OK")
fmt.Println()
}
func ipAddressDump(ch api.Channel, index interface_types.InterfaceIndex) {
- fmt.Printf("Dumping IP addresses for interface index %d\n", index)
+ fmt.Printf("Dumping IP addresses for interface index %d..\n", index)
req := &ip.IPAddressDump{
SwIfIndex: index,
@@ -293,48 +293,6 @@ func interfaceNotifications(ch api.Channel, index interface_types.InterfaceIndex
fmt.Println()
}
-func mactimeDump(conn api.Connection) {
- fmt.Println("Sending mactime dump")
-
- ctx := context.Background()
-
- stream, err := conn.NewStream(ctx)
- if err != nil {
- panic(err)
- }
- defer stream.Close()
-
- if err := stream.SendMsg(&mactime.MactimeDump{}); err != nil {
- logError(err, "sending mactime dump")
- return
- }
-
-Loop:
- for {
- msg, err := stream.RecvMsg()
- if err != nil {
- logError(err, "dumping mactime")
- return
- }
-
- switch msg.(type) {
- case *mactime.MactimeDetails:
- fmt.Printf(" - MactimeDetails: %+v\n", msg)
-
- case *mactime.MactimeDumpReply:
- fmt.Printf(" - MactimeDumpReply: %+v\n", msg)
- break Loop
-
- default:
- logError(err, "unexpected message")
- return
- }
- }
-
- fmt.Println("OK")
- fmt.Println()
-}
-
func marshal(v interface{}) {
fmt.Printf("GO: %#v\n", v)
b, err := json.MarshalIndent(v, "", " ")
@@ -343,3 +301,10 @@ func marshal(v interface{}) {
}
fmt.Printf("JSON: %s\n", b)
}
+
+var errors []error
+
+func logError(err error, msg string) {
+ fmt.Printf("ERROR: %s: %v\n", msg, err)
+ errors = append(errors, err)
+}
diff --git a/examples/stats-client/README.md b/examples/stats-client/README.md
index 0a44a55..a53cf9d 100644
--- a/examples/stats-client/README.md
+++ b/examples/stats-client/README.md
@@ -21,7 +21,7 @@ To enable stats add following section to you VPP config:
## Running example
-First build the example: `go build git.fd.io/govpp.git/examples/stats-api`.
+First build the example: `go build go.fd.io/govpp/examples/stats-api`.
### Higher-level access to stats
diff --git a/examples/stats-client/stats_api.go b/examples/stats-client/stats_api.go
index 288caea..562c2f9 100644
--- a/examples/stats-client/stats_api.go
+++ b/examples/stats-client/stats_api.go
@@ -19,13 +19,14 @@ import (
"fmt"
"log"
"os"
+ "strconv"
"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"
+ "go.fd.io/govpp/adapter"
+ "go.fd.io/govpp/adapter/statsclient"
+ "go.fd.io/govpp/api"
+ "go.fd.io/govpp/core"
)
// ------------------------------------------------------------------
@@ -39,11 +40,12 @@ 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")
+ async = flag.Bool("async", false, "Use asynchronous connection")
)
func init() {
flag.Usage = func() {
- fmt.Fprintf(os.Stderr, "%s: usage [ls|dump|poll|errors|interfaces|nodes|system|buffers] <patterns>...\n", os.Args[0])
+ fmt.Fprintf(os.Stderr, "%s: usage [ls|dump|poll|errors|interfaces|nodes|system|buffers|memory|epoch] <patterns/index>...\n", os.Args[0])
flag.PrintDefaults()
os.Exit(1)
}
@@ -53,16 +55,46 @@ func main() {
flag.Parse()
skipZeros := !*dumpAll
- var patterns []string
+ patterns := make([]string, 0)
+ indexes := make([]uint32, 0)
if flag.NArg() > 0 {
- patterns = flag.Args()[1:]
+ for _, arg := range flag.Args()[1:] {
+ if index, err := strconv.Atoi(arg); err == nil {
+ indexes = append(indexes, uint32(index))
+ continue
+ }
+ patterns = append(patterns, arg)
+ }
}
- client := statsclient.NewStatsClient(*statsSocket)
-
- c, err := core.ConnectStats(client)
- if err != nil {
- log.Fatalln("Connecting failed:", err)
+ var (
+ client *statsclient.StatsClient
+ c *core.StatsConnection
+ err error
+ )
+
+ if *async {
+ var statsChan chan core.ConnectionEvent
+ client = statsclient.NewStatsClient(*statsSocket, statsclient.SetSocketRetryPeriod(1*time.Second),
+ statsclient.SetSocketRetryTimeout(10*time.Second))
+ c, statsChan, err = core.AsyncConnectStats(client, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
+ if err != nil {
+ log.Fatalln("Asynchronous connecting failed:", err)
+ }
+ select {
+ case e := <-statsChan:
+ if e.State == core.Connected {
+ // OK
+ } else {
+ log.Fatalf("VPP stats asynchronous connection failed: %s\n", e.State.String())
+ }
+ }
+ } else {
+ client = statsclient.NewStatsClient(*statsSocket)
+ c, err = core.ConnectStats(client)
+ if err != nil {
+ log.Fatalln("Connecting failed:", err)
+ }
}
defer c.Disconnect()
@@ -114,10 +146,15 @@ func main() {
}
n := 0
for _, counter := range stats.Errors {
- if skipZeros && counter.Value == 0 {
+ var sum uint32
+ for _, valuePerWorker := range counter.Values {
+ sum += uint32(valuePerWorker)
+ }
+
+ if skipZeros && sum == 0 {
continue
}
- fmt.Printf(" - %v\n", counter)
+ fmt.Printf(" - %v %d (per worker: %v)\n", counter.CounterName, sum, counter.Values)
n++
}
fmt.Printf("Listed %d (%d) error counters\n", n, len(stats.Errors))
@@ -129,10 +166,17 @@ func main() {
}
fmt.Printf("Buffer stats: %+v\n", stats)
+ case "memory":
+ stats := new(api.MemoryStats)
+ if err := c.GetMemoryStats(stats); err != nil {
+ log.Fatalln("getting memory stats failed:", err)
+ }
+ fmt.Printf("Memory stats: %+v\n", stats)
+
case "dump":
fmt.Printf("Dumping stats.. %s\n", strings.Join(patterns, " "))
- dumpStats(client, patterns, skipZeros)
+ dumpStats(client, patterns, indexes, skipZeros)
case "poll":
fmt.Printf("Polling stats.. %s\n", strings.Join(patterns, " "))
@@ -142,30 +186,69 @@ func main() {
case "list", "ls", "":
fmt.Printf("Listing stats.. %s\n", strings.Join(patterns, " "))
- listStats(client, patterns)
+ listStats(client, patterns, indexes)
+
+ case "epoch", "e":
+ fmt.Printf("Getting epoch..\n")
+
+ getEpoch(client)
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)
+func listStats(client adapter.StatsAPI, patterns []string, indexes []uint32) {
+ var err error
+ list := make([]adapter.StatIdentifier, 0)
+ if (len(patterns) == 0 && len(indexes) == 0) || len(patterns) != 0 {
+ list, err = client.ListStats(patterns...)
+ if err != nil {
+ log.Fatalln("listing stats failed:", err)
+ }
+ }
+ if len(indexes) != 0 {
+ dir, err := client.PrepareDirOnIndex(indexes...)
+ if err != nil {
+ log.Fatalln("listing stats failed:", err)
+ }
+ for _, onIndexSi := range dir.Entries {
+ list = append(list, onIndexSi.StatIdentifier)
+ }
}
-
for _, stat := range list {
- fmt.Printf(" - %v\n", stat)
+ fmt.Printf(" - %d\t %v\n", stat.Index, string(stat.Name))
}
fmt.Printf("Listed %d stats\n", len(list))
}
-func dumpStats(client adapter.StatsAPI, patterns []string, skipZeros bool) {
- stats, err := client.DumpStats(patterns...)
+func getEpoch(client adapter.StatsAPI) {
+ dir, err := client.PrepareDir()
if err != nil {
- log.Fatalln("dumping stats failed:", err)
+ log.Fatalln("failed to prepare dir in order to read epoch:", err)
+ }
+ d := *dir
+ fmt.Printf("Epoch %d\n", d.Epoch)
+}
+
+func dumpStats(client adapter.StatsAPI, patterns []string, indexes []uint32, skipZeros bool) {
+ var err error
+ stats := make([]adapter.StatEntry, 0)
+ if (len(patterns) == 0 && len(indexes) == 0) || len(patterns) != 0 {
+ stats, err = client.DumpStats(patterns...)
+ if err != nil {
+ log.Fatalln("dumping stats failed:", err)
+ }
+ }
+ if len(indexes) != 0 {
+ dir, err := client.PrepareDirOnIndex(indexes...)
+ if err != nil {
+ log.Fatalln("dumping stats failed:", err)
+ }
+ for _, onIndexSi := range dir.Entries {
+ stats = append(stats, onIndexSi)
+ }
}
n := 0
diff --git a/examples/stream-client/stream_client.go b/examples/stream-client/stream_client.go
new file mode 100644
index 0000000..e36e943
--- /dev/null
+++ b/examples/stream-client/stream_client.go
@@ -0,0 +1,303 @@
+// Copyright (c) 2017 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.
+
+// stream-client is an example VPP management application that exercises the
+// govpp API on real-world use-cases.
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ "go.fd.io/govpp"
+ "go.fd.io/govpp/adapter/socketclient"
+ "go.fd.io/govpp/api"
+ interfaces "go.fd.io/govpp/binapi/interface"
+ "go.fd.io/govpp/binapi/interface_types"
+ "go.fd.io/govpp/binapi/ip"
+ "go.fd.io/govpp/binapi/ip_types"
+ "go.fd.io/govpp/binapi/mactime"
+ "go.fd.io/govpp/binapi/memclnt"
+ "go.fd.io/govpp/binapi/vpe"
+ "go.fd.io/govpp/core"
+)
+
+var (
+ sockAddr = flag.String("sock", socketclient.DefaultSocketName, "Path to VPP binary API socket file")
+)
+
+func main() {
+ flag.Parse()
+
+ fmt.Println("Starting stream client example")
+ fmt.Println()
+
+ // connect to VPP asynchronously
+ conn, connEv, err := govpp.AsyncConnect(*sockAddr, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
+ if err != nil {
+ log.Fatalln("ERROR:", err)
+ }
+ defer conn.Disconnect()
+
+ // wait for Connected event
+ select {
+ case e := <-connEv:
+ if e.State != core.Connected {
+ log.Fatalln("ERROR: connecting to VPP failed:", e.Error)
+ }
+ }
+
+ // check compatibility of used messages
+ ch, err := conn.NewAPIChannel()
+ if err != nil {
+ log.Fatalln("ERROR: creating channel failed:", err)
+ }
+ defer ch.Close()
+ if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
+ log.Fatal(err)
+ }
+ if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
+ log.Fatal(err)
+ }
+
+ // process errors encountered during the example
+ defer func() {
+ if len(errors) > 0 {
+ fmt.Printf("finished with %d errors\n", len(errors))
+ os.Exit(1)
+ } else {
+ fmt.Println("finished successfully")
+ }
+ }()
+
+ // send and receive messages using stream (low-low level API)
+ stream, err := conn.NewStream(context.Background(),
+ core.WithRequestSize(50),
+ core.WithReplySize(50),
+ core.WithReplyTimeout(2*time.Second))
+ if err != nil {
+ panic(err)
+ }
+ defer func() {
+ if err := stream.Close(); err != nil {
+ logError(err, "closing the stream")
+ }
+ }()
+ getVppVersionStream(stream)
+ idx := createLoopbackStream(stream)
+ interfaceDumpStream(stream)
+ addIPAddressStream(stream, idx)
+ ipAddressDumpStream(stream, idx)
+ mactimeDump(stream)
+ return
+}
+
+func getVppVersionStream(stream api.Stream) {
+ fmt.Println("Retrieving version..")
+
+ req := &vpe.ShowVersion{}
+ if err := stream.SendMsg(req); err != nil {
+ logError(err, "ShowVersion sending message")
+ return
+ }
+ recv, err := stream.RecvMsg()
+ if err != nil {
+ logError(err, "ShowVersion receive message")
+ return
+ }
+ recvMsg := recv.(*vpe.ShowVersionReply)
+
+ fmt.Printf("Retrieved VPP version: %q\n", recvMsg.Version)
+ fmt.Println("OK")
+ fmt.Println()
+}
+
+func createLoopbackStream(stream api.Stream) (ifIdx interface_types.InterfaceIndex) {
+ fmt.Println("Creating the loopback interface..")
+
+ req := &interfaces.CreateLoopback{}
+ if err := stream.SendMsg(req); err != nil {
+ logError(err, "CreateLoopback sending message")
+ return
+ }
+ recv, err := stream.RecvMsg()
+ if err != nil {
+ logError(err, "CreateLoopback receive message")
+ return
+ }
+ recvMsg := recv.(*interfaces.CreateLoopbackReply)
+
+ fmt.Printf("Loopback interface index: %v\n", recvMsg.SwIfIndex)
+ fmt.Println("OK")
+ fmt.Println()
+
+ return recvMsg.SwIfIndex
+}
+
+func interfaceDumpStream(stream api.Stream) {
+ fmt.Println("Dumping interfaces..")
+
+ if err := stream.SendMsg(&interfaces.SwInterfaceDump{
+ SwIfIndex: ^interface_types.InterfaceIndex(0),
+ }); err != nil {
+ logError(err, "SwInterfaceDump sending message")
+ return
+ }
+ if err := stream.SendMsg(&memclnt.ControlPing{}); err != nil {
+ logError(err, "ControlPing sending message")
+ return
+ }
+
+Loop:
+ for {
+ msg, err := stream.RecvMsg()
+ if err != nil {
+ logError(err, "SwInterfaceDump receiving message ")
+ return
+ }
+
+ switch msg.(type) {
+ case *interfaces.SwInterfaceDetails:
+ fmt.Printf(" - SwInterfaceDetails: %+v\n", msg)
+
+ case *memclnt.ControlPingReply:
+ fmt.Printf(" - ControlPingReply: %+v\n", msg)
+ break Loop
+
+ default:
+ logError(err, "unexpected message")
+ return
+ }
+ }
+
+ fmt.Println("OK")
+ fmt.Println()
+}
+
+func addIPAddressStream(stream api.Stream, index interface_types.InterfaceIndex) {
+ fmt.Printf("Adding IP address to the interface index %d..\n", index)
+
+ if err := stream.SendMsg(&interfaces.SwInterfaceAddDelAddress{
+ SwIfIndex: index,
+ IsAdd: true,
+ Prefix: ip_types.AddressWithPrefix{
+ Address: ip_types.Address{
+ Af: ip_types.ADDRESS_IP4,
+ Un: ip_types.AddressUnionIP4(ip_types.IP4Address{10, 10, 0, uint8(index)}),
+ },
+ Len: 32,
+ },
+ }); err != nil {
+ logError(err, "SwInterfaceAddDelAddress sending message")
+ return
+ }
+
+ recv, err := stream.RecvMsg()
+ if err != nil {
+ logError(err, "SwInterfaceAddDelAddressReply receiving message")
+ return
+ }
+ recvMsg := recv.(*interfaces.SwInterfaceAddDelAddressReply)
+
+ fmt.Printf("Added IP address to interface: %v (return value: %d)\n", index, recvMsg.Retval)
+ fmt.Println("OK")
+ fmt.Println()
+}
+
+func ipAddressDumpStream(stream api.Stream, index interface_types.InterfaceIndex) {
+ fmt.Printf("Dumping IP addresses for interface index %d..\n", index)
+
+ if err := stream.SendMsg(&ip.IPAddressDump{
+ SwIfIndex: index,
+ }); err != nil {
+ logError(err, "IPAddressDump sending message")
+ return
+ }
+ if err := stream.SendMsg(&memclnt.ControlPing{}); err != nil {
+ logError(err, "ControlPing sending sending message")
+ return
+ }
+
+Loop:
+ for {
+ msg, err := stream.RecvMsg()
+ if err != nil {
+ logError(err, "IPAddressDump receiving message ")
+ return
+ }
+
+ switch msg.(type) {
+ case *ip.IPAddressDetails:
+ fmt.Printf(" - IPAddressDetails: %+v\n", msg)
+
+ case *memclnt.ControlPingReply:
+ fmt.Printf(" - ControlPingReply: %+v\n", msg)
+ break Loop
+
+ default:
+ logError(err, "unexpected message")
+ return
+ }
+ }
+
+ fmt.Println("OK")
+ fmt.Println()
+}
+
+// Mactime dump uses MactimeDumpReply message as an end of the stream
+// notification instead of the control ping.
+func mactimeDump(stream api.Stream) {
+ fmt.Println("Sending mactime dump..")
+
+ if err := stream.SendMsg(&mactime.MactimeDump{}); err != nil {
+ logError(err, "sending mactime dump")
+ return
+ }
+
+Loop:
+ for {
+ msg, err := stream.RecvMsg()
+ if err != nil {
+ logError(err, "MactimeDump receiving message")
+ return
+ }
+
+ switch msg.(type) {
+ case *mactime.MactimeDetails:
+ fmt.Printf(" - MactimeDetails: %+v\n", msg)
+
+ case *mactime.MactimeDumpReply:
+ fmt.Printf(" - MactimeDumpReply: %+v\n", msg)
+ break Loop
+
+ default:
+ logError(err, "unexpected message")
+ return
+ }
+ }
+
+ fmt.Println("OK")
+ fmt.Println()
+}
+
+var errors []error
+
+func logError(err error, msg string) {
+ fmt.Printf("ERROR: %s: %v\n", msg, err)
+ errors = append(errors, err)
+}