aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CHANGELOG.md6
-rw-r--r--examples/multi-vpp/multi_vpp.go220
-rw-r--r--govpp.go26
4 files changed, 231 insertions, 22 deletions
diff --git a/.gitignore b/.gitignore
index 81b7a5c..8a782d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@ cmd/binapi-generator/binapi-generator
cmd/vpp-proxy/vpp-proxy
# examples
+examples/multi-vpp/multi-vpp
examples/perf-bench/perf-bench
examples/rpc-service/rpc-service
examples/simple-client/simple-client
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2c0ca09..44a8c2d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,10 +23,14 @@ This file lists changes for the GoVPP releases.
- dependency on `github.com/lunixbochs/struc` was removed and message un/marshaling is now part of generated code
- generated code now contains comment with information about versions of VPP and binapi-generator
- RPC service code is now generated into a separated file (`*_rpc.ba.go`) in same directory
+- many generated aliases were removed and referenced to `*_types` files for simpler reading
+- generated new helper methods for more convenient IP and MAC address conversion
### Features
- optimized [socketclient](adapter/socketclient) adapter and add method to set client name
- added list of compatible messages to `CompatibilityError`
+- removed global binary API adapter - this change allows GoVPP to manage multiple VPP connections with different
+ sockets simultaneously
### Fixes
- `MsgCodec` will recover panic occurring during a message decoding
@@ -39,6 +43,8 @@ This file lists changes for the GoVPP releases.
- added more code samples of working with unions in [union example](examples/union-example)
- added profiling mode to [perf bench](examples/perf-bench) example
- improved [simple client](examples/simple-client) example to work properly even with multiple runs
+- added [multi-vpp](examples/multi-vpp) example displaying management of two VPP instances from single
+ application
#### Dependencies
- updated `github.com/sirupsen/logrus` dep to `v1.6.0`
diff --git a/examples/multi-vpp/multi_vpp.go b/examples/multi-vpp/multi_vpp.go
new file mode 100644
index 0000000..244dd03
--- /dev/null
+++ b/examples/multi-vpp/multi_vpp.go
@@ -0,0 +1,220 @@
+// Copyright (c) 2020 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.
+
+// multi-vpp is an example of managing multiple VPPs in single application.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "git.fd.io/govpp.git"
+ "git.fd.io/govpp.git/adapter/socketclient"
+ "git.fd.io/govpp.git/api"
+ "git.fd.io/govpp.git/core"
+ "git.fd.io/govpp.git/examples/binapi/interface_types"
+ "git.fd.io/govpp.git/examples/binapi/interfaces"
+ "git.fd.io/govpp.git/examples/binapi/ip"
+ "git.fd.io/govpp.git/examples/binapi/ip_types"
+ "git.fd.io/govpp.git/examples/binapi/vpe"
+ "log"
+ "os"
+)
+
+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")
+)
+
+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")
+ }
+
+ // connect VPP1
+ conn1, err := connectToVPP(*sockAddrVpp1, 1)
+ if err != nil {
+ log.Fatalf("ERROR: connecting VPP failed (socket %s): %v\n", *sockAddrVpp1, err)
+ }
+ defer conn1.Disconnect()
+ ch1, err := getAPIChannel(conn1)
+ if err != nil {
+ log.Fatalf("ERROR: creating channel failed (socket: %s): %v\n", *sockAddrVpp1, err)
+ }
+ defer ch1.Close()
+
+ // connect VPP2
+ conn2, err := connectToVPP(*sockAddrVpp2, 2)
+ if err != nil {
+ log.Fatalf("ERROR: connecting VPP failed (socket %s): %v\n", *sockAddrVpp2, err)
+ }
+ defer conn2.Disconnect()
+ ch2, err := getAPIChannel(conn2)
+ if err != nil {
+ log.Fatalf("ERROR: creating channel failed (socket: %s): %v\n", *sockAddrVpp2, 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)
+
+ if len(Errors) > 0 {
+ fmt.Printf("finished with %d errors\n", len(Errors))
+ os.Exit(1)
+ } else {
+ fmt.Println("finished successfully")
+ }
+}
+
+func connectToVPP(socket string, attempts int) (*core.Connection, error) {
+ connection, event, err := govpp.AsyncConnect(socket, attempts, core.DefaultReconnectInterval)
+ if err != nil {
+ return nil, err
+ }
+
+ // handle connection event
+ select {
+ case e := <-event:
+ if e.State != core.Connected {
+ return nil, err
+ }
+ }
+ return connection, nil
+}
+
+func getAPIChannel(conn *core.Connection) (api.Channel, error) {
+ ch, err := conn.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
+ }
+ return ch, nil
+}
+
+// getVppVersion returns VPP version (simple API usage)
+func getVppVersion(ch api.Channel) {
+ fmt.Println("Retrieving version")
+
+ req := &vpe.ShowVersion{}
+ reply := &vpe.ShowVersionReply{}
+
+ if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
+ logError(err, "retrieving version")
+ return
+ }
+ fmt.Printf("reply: %+v\n", reply)
+
+ fmt.Printf("VPP version: %q\n", reply.Version)
+ 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)
+}
+
+// createLoopback sends request to create a loopback interface
+func createLoopback(ch api.Channel) interface_types.InterfaceIndex {
+ fmt.Println("Adding loopback interface")
+
+ req := &interfaces.CreateLoopback{}
+ reply := &interfaces.CreateLoopbackReply{}
+
+ if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
+ 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()
+
+ 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
+ }
+
+
+ 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")
+ fmt.Println()
+}
+
+func retrieveIPAddresses(ch api.Channel, index interface_types.InterfaceIndex) {
+ fmt.Printf("Retrieving IP addresses for interface index %d\n", index)
+
+ req := &ip.IPAddressDump{
+ SwIfIndex: index,
+ }
+ reqCtx := ch.SendMultiRequest(req)
+
+ for {
+ msg := &ip.IPAddressDetails{}
+ stop, err := reqCtx.ReceiveReply(msg)
+ if err != nil {
+ logError(err, "dumping IP addresses")
+ return
+ }
+ if stop {
+ break
+ }
+ prefix := ip_types.Prefix(msg.Prefix)
+ fmt.Printf(" - ip address: %+v\n", prefix.ToString())
+ }
+
+ fmt.Println("OK")
+ fmt.Println()
+}
diff --git a/govpp.go b/govpp.go
index f36ce40..822739c 100644
--- a/govpp.go
+++ b/govpp.go
@@ -17,40 +17,22 @@ package govpp
import (
"time"
- "git.fd.io/govpp.git/adapter"
"git.fd.io/govpp.git/adapter/socketclient"
"git.fd.io/govpp.git/core"
)
-var (
- // VPP binary API adapter that will be used in the subsequent Connect calls
- vppAdapter adapter.VppAPI
-)
-
-func getVppAdapter(addr string) adapter.VppAPI {
- if vppAdapter == nil {
- vppAdapter = socketclient.NewVppClient(addr)
- }
- return vppAdapter
-}
-
-// SetVppAdapter sets the adapter that will be used for connections to VPP in the subsequent `Connect` calls.
-func SetVppAdapter(a adapter.VppAPI) {
- vppAdapter = a
-}
-
// Connect connects the govpp core to VPP either using the default VPP Adapter, or using the adapter previously
// set by SetAdapter (useful mostly just for unit/integration tests with mocked VPP adapter).
// This call blocks until VPP is connected, or an error occurs. Only one connection attempt will be performed.
func Connect(shm string) (*core.Connection, error) {
- return core.Connect(getVppAdapter(shm))
+ return core.Connect(socketclient.NewVppClient(shm))
}
// AsyncConnect asynchronously connects the govpp core to VPP either using the default VPP Adapter,
// or using the adapter previously set by SetAdapter.
-// This call does not block until connection is established, it returns immediately. The caller is
+// This call does not block until the connection is established, it returns immediately. The caller is
// supposed to watch the returned ConnectionState channel for Connected/Disconnected events.
-// In case of disconnect, the library will asynchronously try to reconnect.
+// In case of a disconnect, the library will asynchronously try to reconnect.
func AsyncConnect(shm string, attempts int, interval time.Duration) (*core.Connection, chan core.ConnectionEvent, error) {
- return core.AsyncConnect(getVppAdapter(shm), attempts, interval)
+ return core.AsyncConnect(socketclient.NewVppClient(shm), attempts, interval)
}