aboutsummaryrefslogtreecommitdiffstats
path: root/examples/multi-vpp/multi_vpp.go
diff options
context:
space:
mode:
Diffstat (limited to 'examples/multi-vpp/multi_vpp.go')
-rw-r--r--examples/multi-vpp/multi_vpp.go311
1 files changed, 221 insertions, 90 deletions
diff --git a/examples/multi-vpp/multi_vpp.go b/examples/multi-vpp/multi_vpp.go
index 8714c9a..c42f802 100644
--- a/examples/multi-vpp/multi_vpp.go
+++ b/examples/multi-vpp/multi_vpp.go
@@ -20,9 +20,11 @@ import (
"fmt"
"log"
"os"
+ "strings"
"git.fd.io/govpp.git"
"git.fd.io/govpp.git/adapter/socketclient"
+ "git.fd.io/govpp.git/adapter/statsclient"
"git.fd.io/govpp.git/api"
interfaces "git.fd.io/govpp.git/binapi/interface"
"git.fd.io/govpp.git/binapi/interface_types"
@@ -33,89 +35,157 @@ import (
)
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)
+}
- // connect VPP2
- conn2, err := connectToVPP(*sockAddrVpp2, 2)
+func connectVPP(name, binapiSocket, statsSocket string) (api.Channel, api.StatsProvider, func()) {
+ fmt.Println()
+ logHeader("Connecting to %s", name)
+
+ // 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")
+ logInfo("OK\n")
- // retrieve configuration from the VPPs
- retrieveIPAddresses(ch1, ifIdx1)
- retrieveIPAddresses(ch2, ifIdx2)
-
- 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
}
}
- return connection, nil
+ ch, err := getAPIChannel(conn)
+ if err != nil {
+ return nil, nil, err
+ }
+ disconnect := func() {
+ if ch != nil {
+ ch.Close()
+ }
+ if conn != nil {
+ conn.Disconnect()
+ }
+ }
+ 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)
+ }
+
+ 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)
}
- fmt.Println("OK")
+ 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)
+}