From 2fdb2acee79b43af3e4edb7cac4a5cae428f587e Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Thu, 9 Jul 2020 10:02:41 +0200 Subject: Removed global binapi VPP adapter * added example showing management of 2 VPP instances with different sockets * updated changelog Change-Id: I531eda8f055cc2a24ba2210217e70a8ad42a47c0 Signed-off-by: Vladimir Lavor --- .gitignore | 1 + CHANGELOG.md | 6 ++ examples/multi-vpp/multi_vpp.go | 220 ++++++++++++++++++++++++++++++++++++++++ govpp.go | 26 +---- 4 files changed, 231 insertions(+), 22 deletions(-) create mode 100644 examples/multi-vpp/multi_vpp.go 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) } -- cgit 1.2.3-korg