From 9ea1f778fb1458ce6b2265941885eab0b34b33d7 Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Mon, 12 Oct 2020 14:21:05 +0200 Subject: Stream API options * Stream API uses the same default values as the Channel API * request size, reply size and reply timeout settable using functional options * Added stream client example to show the stream API usage Change-Id: Id599134a7f520fc19f7d770ed5e3de74a7936829 Signed-off-by: Vladimir Lavor --- examples/stream-client/stream_client.go | 302 ++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 examples/stream-client/stream_client.go (limited to 'examples/stream-client/stream_client.go') diff --git a/examples/stream-client/stream_client.go b/examples/stream-client/stream_client.go new file mode 100644 index 0000000..fadfe23 --- /dev/null +++ b/examples/stream-client/stream_client.go @@ -0,0 +1,302 @@ +// 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" + + "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" +) + +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(&vpe.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 *vpe.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(&vpe.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 *vpe.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) +} -- cgit 1.2.3-korg