From 280b1c6c83b676ef4e592f4ecf60cb5b54b6a753 Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Tue, 16 Jun 2020 10:40:34 +0200 Subject: Optimize socketclient adapter and add various code improvements This commit includes: Features - optimized [socketclient](adapter/socketclient) adapter and add method to set client name - added list of compatible messages to `CompatibilityError` Fixes - `MsgCodec` will recover panic occurring during a message decoding - calling `Unsubscibe` will close the notification channel Other - improved log messages to provide more relevant info Examples - 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 Dependencies - updated `github.com/sirupsen/logrus` dep to `v1.6.0` - updated `github.com/lunixbochs/struc` dep to `v0.0.0-20200521075829-a4cb8d33dbbe` Change-Id: I136a3968ccf9e93760d7ee2b9902fc7e6390a09d Signed-off-by: Ondrej Fabry --- examples/perf-bench/perf-bench.go | 23 +++++--- examples/simple-client/simple_client.go | 78 ++++++++++++++---------- examples/union-example/union_example.go | 101 +++++++++++++++++--------------- 3 files changed, 118 insertions(+), 84 deletions(-) (limited to 'examples') diff --git a/examples/perf-bench/perf-bench.go b/examples/perf-bench/perf-bench.go index f48c154..81d183c 100644 --- a/examples/perf-bench/perf-bench.go +++ b/examples/perf-bench/perf-bench.go @@ -20,6 +20,7 @@ import ( "flag" "fmt" "log" + "os" "time" "github.com/pkg/profile" @@ -39,14 +40,14 @@ const ( func main() { // parse optional flags - var sync, prof bool + var sync bool var cnt int - var sock string + var sock, prof string flag.BoolVar(&sync, "sync", false, "run synchronous perf test") - flag.StringVar(&sock, "socket", socketclient.DefaultSocketName, "Path to VPP API socket") - flag.String("socket", statsclient.DefaultSocketName, "Path to VPP stats socket") + flag.StringVar(&sock, "api-socket", socketclient.DefaultSocketName, "Path to VPP API socket") + flag.String("stats-socket", statsclient.DefaultSocketName, "Path to VPP stats socket") flag.IntVar(&cnt, "count", 0, "count of requests to be sent to VPP") - flag.BoolVar(&prof, "prof", false, "generate profile data") + flag.StringVar(&prof, "prof", "", "enable profiling mode [mem, cpu]") flag.Parse() if cnt == 0 { @@ -58,8 +59,16 @@ func main() { } } - if prof { - defer profile.Start().Stop() + switch prof { + case "mem": + defer profile.Start(profile.MemProfile, profile.MemProfileRate(1)).Stop() + case "cpu": + defer profile.Start(profile.CPUProfile).Stop() + case "": + default: + fmt.Printf("invalid profiling mode: %q\n", prof) + flag.Usage() + os.Exit(1) } a := socketclient.NewVppClient(sock) diff --git a/examples/simple-client/simple_client.go b/examples/simple-client/simple_client.go index 6d96ca8..fe7c109 100644 --- a/examples/simple-client/simple_client.go +++ b/examples/simple-client/simple_client.go @@ -65,20 +65,22 @@ func main() { } 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) } - createLoopback(ch) - createLoopback(ch) + idx := createLoopback(ch) interfaceDump(ch) - addIPAddress(ch) - ipAddressDump(ch) - - interfaceNotifications(ch) + addIPAddress(ch, idx) + ipAddressDump(ch, idx) + interfaceNotifications(ch, idx) if len(Errors) > 0 { fmt.Printf("finished with %d errors\n", len(Errors)) @@ -109,11 +111,12 @@ func vppVersion(ch api.Channel) { fmt.Printf("reply: %+v\n", reply) fmt.Printf("VPP version: %q\n", cleanString(reply.Version)) - fmt.Println("ok") + fmt.Println("OK") + fmt.Println() } // createLoopback sends request to create loopback interface. -func createLoopback(ch api.Channel) { +func createLoopback(ch api.Channel) interfaces.InterfaceIndex { fmt.Println("Creating loopback interface") req := &interfaces.CreateLoopback{} @@ -121,48 +124,54 @@ func createLoopback(ch api.Channel) { if err := ch.SendRequest(req).ReceiveReply(reply); err != nil { logError(err, "creating loopback interface") - return + return 0 } fmt.Printf("reply: %+v\n", reply) - fmt.Printf("loopback interface index: %v\n", reply.SwIfIndex) + fmt.Printf("interface index: %v\n", reply.SwIfIndex) fmt.Println("OK") + fmt.Println() + + return reply.SwIfIndex } // interfaceDump shows an example of multipart request (multiple replies are expected). func interfaceDump(ch api.Channel) { fmt.Println("Dumping interfaces") + n := 0 reqCtx := ch.SendMultiRequest(&interfaces.SwInterfaceDump{}) for { msg := &interfaces.SwInterfaceDetails{} stop, err := reqCtx.ReceiveReply(msg) + if stop { + break + } if err != nil { logError(err, "dumping interfaces") return } - if stop { - break - } - fmt.Printf(" - interface: %+v\n", msg) + n++ + fmt.Printf(" - interface #%d: %+v\n", n, msg) } fmt.Println("OK") + fmt.Println() } // addIPAddress sends request to add IP address to interface. -func addIPAddress(ch api.Channel) { - fmt.Println("Adding IP address to interface") +func addIPAddress(ch api.Channel, index interfaces.InterfaceIndex) { + fmt.Printf("Adding IP address to interface to interface index %d\n", index) req := &interfaces.SwInterfaceAddDelAddress{ - SwIfIndex: 1, + SwIfIndex: index, IsAdd: true, Prefix: ip_types.AddressWithPrefix{ Address: interfaces.Address{ Af: ip_types.ADDRESS_IP4, - Un: ip_types.AddressUnionIP4(interfaces.IP4Address{10, 10, 0, 1}), + Un: ip_types.AddressUnionIP4(interfaces.IP4Address{10, 10, 0, uint8(index)}), }, - Len: 24, + Len: 32, }, } reply := &interfaces.SwInterfaceAddDelAddressReply{} @@ -174,13 +183,14 @@ func addIPAddress(ch api.Channel) { fmt.Printf("reply: %+v\n", reply) fmt.Println("OK") + fmt.Println() } -func ipAddressDump(ch api.Channel) { - fmt.Println("Dumping IP addresses") +func ipAddressDump(ch api.Channel, index interfaces.InterfaceIndex) { + fmt.Printf("Dumping IP addresses for interface index %d\n", index) req := &ip.IPAddressDump{ - SwIfIndex: 1, + SwIfIndex: index, } reqCtx := ch.SendMultiRequest(req) @@ -198,13 +208,14 @@ func ipAddressDump(ch api.Channel) { } fmt.Println("OK") + fmt.Println() } // interfaceNotifications shows the usage of notification API. Note that for notifications, // you are supposed to create your own Go channel with your preferred buffer size. If the channel's // buffer is full, the notifications will not be delivered into it. -func interfaceNotifications(ch api.Channel) { - fmt.Println("Subscribing to notificaiton events") +func interfaceNotifications(ch api.Channel, index interfaces.InterfaceIndex) { + fmt.Printf("Subscribing to notificaiton events for interface index %d\n", index) notifChan := make(chan api.Message, 100) @@ -225,27 +236,31 @@ func interfaceNotifications(ch api.Channel) { return } + // receive notifications + go func() { + for notif := range notifChan { + fmt.Printf("incoming event: %+v\n", notif.(*interfaces.SwInterfaceEvent)) + } + }() + // generate some events in VPP err = ch.SendRequest(&interfaces.SwInterfaceSetFlags{ - SwIfIndex: 1, + SwIfIndex: index, + Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP, }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{}) if err != nil { logError(err, "setting interface flags") return } err = ch.SendRequest(&interfaces.SwInterfaceSetFlags{ - SwIfIndex: 1, - Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP, + SwIfIndex: index, + Flags: 0, }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{}) if err != nil { logError(err, "setting interface flags") return } - // receive one notification - notif := (<-notifChan).(*interfaces.SwInterfaceEvent) - fmt.Printf("incoming event: %+v\n", notif) - // disable interface events in VPP err = ch.SendRequest(&interfaces.WantInterfaceEvents{ PID: uint32(os.Getpid()), @@ -263,6 +278,7 @@ func interfaceNotifications(ch api.Channel) { return } + fmt.Println("OK") fmt.Println() } diff --git a/examples/union-example/union_example.go b/examples/union-example/union_example.go index 92c3ec2..9993ee1 100644 --- a/examples/union-example/union_example.go +++ b/examples/union-example/union_example.go @@ -16,73 +16,82 @@ package main import ( - "bytes" "fmt" "log" "net" + "reflect" + "git.fd.io/govpp.git/codec" "git.fd.io/govpp.git/examples/binapi/ip" "git.fd.io/govpp.git/examples/binapi/ip_types" - - "github.com/lunixbochs/struc" ) +func init() { + log.SetFlags(0) +} + func main() { + constructExample() + encodingExample() - usageExample() + + // convert IP from string form into Address type containing union + convertIP("10.10.1.1") + convertIP("ff80::1") } -func encodingExample() { - // create union with IPv4 address - var unionIP4 ip.AddressUnion - unionIP4.SetIP4(ip.IP4Address{192, 168, 1, 10}) - - // use it in the Address type - addr := &ip.Address{ - Af: ip_types.ADDRESS_IP4, - Un: ip_types.AddressUnionIP4(ip.IP4Address{192, 168, 1, 10}), - } - log.Printf("encoding union IPv4: %v", addr.Un.GetIP4()) +func constructExample() { + var union ip_types.AddressUnion - // encode the address with union - data := encode(addr) - // decode the address with union - addr2 := decode(data) + // create AddressUnion with AdressUnionXXX constructors + union = ip_types.AddressUnionIP4(ip.IP4Address{192, 168, 1, 10}) + union = ip_types.AddressUnionIP6(ip.IP6Address{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02}) - log.Printf("decoded union IPv4: %v", addr2.Un.GetIP4()) + // set AddressUnion with SetXXX methods + union.SetIP4(ip.IP4Address{192, 168, 1, 10}) + union.SetIP6(ip.IP6Address{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02}) } -func encode(addr *ip.Address) []byte { - log.Printf("encoding address: %#v", addr) - buf := new(bytes.Buffer) - if err := struc.Pack(buf, addr); err != nil { - panic(err) +func encodingExample() { + var c codec.MsgCodec + + // encode this message + var msg = ip.IPPuntRedirect{ + Punt: ip.PuntRedirect{ + Nh: ip_types.Address{ + Af: ip_types.ADDRESS_IP4, + Un: ip_types.AddressUnionIP4(ip.IP4Address{192, 168, 1, 10}), + }, + }, + IsAdd: true, } - return buf.Bytes() -} + log.Printf("encoding message: %+v", msg) -func decode(data []byte) *ip.Address { - addr := new(ip.Address) - buf := bytes.NewReader(data) - if err := struc.Unpack(buf, addr); err != nil { - panic(err) + b, err := c.EncodeMsg(&msg, 1) + if err != nil { + log.Fatal(err) } - log.Printf("decoded address: %#v", addr) - return addr -} -func usageExample() { - var convAddr = func(ip string) { - addr, err := ipToAddress(ip) - if err != nil { - log.Printf("converting ip %q failed: %v", ip, err) - } - fmt.Printf("% 0X\n", addr) + // decode into this message + var msg2 ip.IPPuntRedirect + if err := c.DecodeMsg(b, &msg2); err != nil { + log.Fatal(err) } + log.Printf("decoded message: %+v", msg2) - convAddr("10.10.10.10") - convAddr("::1") - convAddr("") + // compare the messages + if !reflect.DeepEqual(msg, msg2) { + log.Fatal("messages are not equal") + } +} + +func convertIP(ip string) { + addr, err := ipToAddress(ip) + if err != nil { + log.Printf("error converting IP: %v", err) + return + } + fmt.Printf("converted IP %q to: %+v\n", ip, addr) } func ipToAddress(ipstr string) (addr ip.Address, err error) { @@ -98,7 +107,7 @@ func ipToAddress(ipstr string) (addr ip.Address, err error) { } else { addr.Af = ip_types.ADDRESS_IP4 var ip4addr ip.IP4Address - copy(ip4addr[:], ip4) + copy(ip4addr[:], ip4.To4()) addr.Un.SetIP4(ip4addr) } return -- cgit 1.2.3-korg