X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=examples%2Fsimple-client%2Fsimple_client.go;h=fea3e43bb26f8a299282686464862644309887d7;hb=67cea0df30f7ef348e265c9326f7a9f15ba26240;hp=08d4da6a4bfdf34d54cf9295333ff0be88dfdbae;hpb=fa21c9d726ebb807895a8571af9a16dab5cd8d6e;p=govpp.git diff --git a/examples/simple-client/simple_client.go b/examples/simple-client/simple_client.go index 08d4da6..fea3e43 100644 --- a/examples/simple-client/simple_client.go +++ b/examples/simple-client/simple_client.go @@ -17,127 +17,138 @@ package main import ( + "encoding/json" + "flag" "fmt" "log" - "net" "os" - "strings" - "git.fd.io/govpp.git" - "git.fd.io/govpp.git/api" - "git.fd.io/govpp.git/examples/bin_api/acl" - "git.fd.io/govpp.git/examples/bin_api/interfaces" - "git.fd.io/govpp.git/examples/bin_api/ip" + "go.fd.io/govpp" + "go.fd.io/govpp/adapter/socketclient" + "go.fd.io/govpp/api" + interfaces "go.fd.io/govpp/binapi/interface" + "go.fd.io/govpp/binapi/interface_types" + "go.fd.io/govpp/binapi/ip" + "go.fd.io/govpp/binapi/ip_types" + "go.fd.io/govpp/binapi/vpe" + "go.fd.io/govpp/core" +) + +var ( + sockAddr = flag.String("sock", socketclient.DefaultSocketName, "Path to VPP binary API socket file") ) func main() { - fmt.Println("Starting simple VPP client...") + flag.Parse() + + fmt.Println("Starting simple client example") + fmt.Println() - // connect to VPP - conn, err := govpp.Connect("") + // connect to VPP asynchronously + conn, connEv, err := govpp.AsyncConnect(*sockAddr, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval) if err != nil { log.Fatalln("ERROR:", err) } defer conn.Disconnect() - // create an API channel that will be used in the examples + // 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:", err) + 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) + } - // individual examples - aclVersion(ch) - aclConfig(ch) - aclDump(ch) + // 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") + } + }() + // use request/reply (channel API) + getVppVersion(ch) + getSystemTime(ch) + idx := createLoopback(ch) interfaceDump(ch) - ipAddressDump(ch) - - setIpUnnumbered(ch) - ipUnnumberedDump(ch) - - interfaceNotifications(ch) + addIPAddress(ch, idx) + ipAddressDump(ch, idx) + interfaceNotifications(ch, idx) } -// aclVersion is the simplest API example - one empty request message and one reply message. -func aclVersion(ch api.Channel) { - fmt.Println("ACL getting version") +func getVppVersion(ch api.Channel) { + fmt.Println("Retrieving version..") - req := &acl.ACLPluginGetVersion{} - reply := &acl.ACLPluginGetVersionReply{} + req := &vpe.ShowVersion{} + reply := &vpe.ShowVersionReply{} if err := ch.SendRequest(req).ReceiveReply(reply); err != nil { - fmt.Println("ERROR:", err) - } else { - fmt.Printf("ACL version reply: %+v\n", reply) + logError(err, "retrieving version") + return } + + fmt.Printf("VPP version: %q\n", reply.Version) + fmt.Println("OK") + fmt.Println() } -// aclConfig is another simple API example - in this case, the request contains structured data. -func aclConfig(ch api.Channel) { - fmt.Println("ACL adding replace") - - req := &acl.ACLAddReplace{ - ACLIndex: ^uint32(0), - Tag: []byte("access list 1"), - R: []acl.ACLRule{ - { - IsPermit: 1, - SrcIPAddr: net.ParseIP("10.0.0.0").To4(), - SrcIPPrefixLen: 8, - DstIPAddr: net.ParseIP("192.168.1.0").To4(), - DstIPPrefixLen: 24, - Proto: 6, - }, - { - IsPermit: 1, - SrcIPAddr: net.ParseIP("8.8.8.8").To4(), - SrcIPPrefixLen: 32, - DstIPAddr: net.ParseIP("172.16.0.0").To4(), - DstIPPrefixLen: 16, - Proto: 6, - }, - }, - } - reply := &acl.ACLAddReplaceReply{} +func getSystemTime(ch api.Channel) { + fmt.Println("Retrieving system time..") + + req := &vpe.ShowVpeSystemTime{} + reply := &vpe.ShowVpeSystemTimeReply{} if err := ch.SendRequest(req).ReceiveReply(reply); err != nil { - fmt.Println("ERROR:", err) - return - } - if reply.Retval != 0 { - fmt.Println("Retval:", reply.Retval) + logError(err, "retrieving system time") return } - fmt.Printf("ACL add replace reply: %+v\n", reply) - + fmt.Printf("system time: %v\n", reply.VpeSystemTime) + fmt.Println("OK") + fmt.Println() } -// aclDump shows an example where SendRequest and ReceiveReply are not chained together. -func aclDump(ch api.Channel) { - fmt.Println("Dumping ACL") - - req := &acl.ACLDump{} - reply := &acl.ACLDetails{} +func createLoopback(ch api.Channel) interface_types.InterfaceIndex { + fmt.Println("Creating loopback interface..") - reqCtx := ch.SendRequest(req) + req := &interfaces.CreateLoopback{} + reply := &interfaces.CreateLoopbackReply{} - if err := reqCtx.ReceiveReply(reply); err != nil { - fmt.Println("ERROR:", err) - } else { - fmt.Printf("ACL details: %+v\n", reply) + if err := ch.SendRequest(req).ReceiveReply(reply); err != nil { + logError(err, "creating loopback interface") + return 0 } + + 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") - - reqCtx := ch.SendMultiRequest(&interfaces.SwInterfaceDump{}) + fmt.Println("Dumping interfaces..") + n := 0 + reqCtx := ch.SendMultiRequest(&interfaces.SwInterfaceDump{ + SwIfIndex: ^interface_types.InterfaceIndex(0), + }) for { msg := &interfaces.SwInterfaceDetails{} stop, err := reqCtx.ReceiveReply(msg) @@ -145,84 +156,83 @@ func interfaceDump(ch api.Channel) { break } if err != nil { - fmt.Println("ERROR:", err) + logError(err, "dumping interfaces") + return } - ifaceName := strings.TrimFunc(string(msg.InterfaceName), func(r rune) bool { - return r == 0x00 - }) - fmt.Printf("Interface %q: %+v\n", ifaceName, msg) - } -} - -func ipAddressDump(ch api.Channel) { - fmt.Println("Dumping IP addresses") - - req := &ip.IPAddressDump{ - SwIfIndex: 1, //^uint32(0), + n++ + fmt.Printf(" - interface #%d: %+v\n", n, msg) + marshal(msg) } - reqCtx := ch.SendMultiRequest(req) - for { - msg := &ip.IPAddressDetails{} - stop, err := reqCtx.ReceiveReply(msg) - if stop { - break - } - if err != nil { - fmt.Println("ERROR:", err) - } - fmt.Printf("ip address details: %d %+v\n", msg.SwIfIndex, msg) - } + fmt.Println("OK") + fmt.Println() } -// aclDump shows an example where SendRequest and ReceiveReply are not chained together. -func setIpUnnumbered(ch api.Channel) { - req := &interfaces.SwInterfaceSetUnnumbered{ - SwIfIndex: 1, - UnnumberedSwIfIndex: 2, - IsAdd: 1, +func addIPAddress(ch api.Channel, index interface_types.InterfaceIndex) { + fmt.Printf("Adding IP address to interface index %d\n", index) + + req := &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, + }, } - reply := &interfaces.SwInterfaceSetUnnumberedReply{} + marshal(req) + reply := &interfaces.SwInterfaceAddDelAddressReply{} if err := ch.SendRequest(req).ReceiveReply(reply); err != nil { - fmt.Println("ERROR:", err) - } else { - fmt.Printf("%+v\n", reply) + logError(err, "adding IP address to interface") + return } + + fmt.Println("OK") + fmt.Println() } -func ipUnnumberedDump(ch api.Channel) { - fmt.Println("Dumping IP unnumbered") +func ipAddressDump(ch api.Channel, index interface_types.InterfaceIndex) { + fmt.Printf("Dumping IP addresses for interface index %d..\n", index) - reqCtx := ch.SendMultiRequest(&ip.IPUnnumberedDump{ - SwIfIndex: ^uint32(0), - }) + req := &ip.IPAddressDump{ + SwIfIndex: index, + } + reqCtx := ch.SendMultiRequest(req) for { - msg := &ip.IPUnnumberedDetails{} + msg := &ip.IPAddressDetails{} stop, err := reqCtx.ReceiveReply(msg) + if err != nil { + logError(err, "dumping IP addresses") + return + } if stop { break } - if err != nil { - fmt.Println("ERROR:", err) - } - fmt.Printf("IP unnumbered details: %+v\n", msg) + fmt.Printf(" - ip address: %+v\n", msg) + marshal(msg) } + + 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 interface_types.InterfaceIndex) { + fmt.Printf("Subscribing to notificaiton events for interface index %d\n", index) notifChan := make(chan api.Message, 100) // subscribe for specific notification message sub, err := ch.SubscribeNotification(notifChan, &interfaces.SwInterfaceEvent{}) if err != nil { - panic(err) + logError(err, "subscribing to interface events") + return } // enable interface events in VPP @@ -231,41 +241,70 @@ func interfaceNotifications(ch api.Channel) { EnableDisable: 1, }).ReceiveReply(&interfaces.WantInterfaceEventsReply{}) if err != nil { - panic(err) + logError(err, "enabling interface events") + return } + // receive notifications + go func() { + for notif := range notifChan { + e := notif.(*interfaces.SwInterfaceEvent) + fmt.Printf("incoming event: %+v\n", e) + marshal(e) + } + }() + // generate some events in VPP err = ch.SendRequest(&interfaces.SwInterfaceSetFlags{ - SwIfIndex: 0, - AdminUpDown: 0, + SwIfIndex: index, + Flags: interface_types.IF_STATUS_API_FLAG_ADMIN_UP, }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{}) if err != nil { - panic(err) + logError(err, "setting interface flags") + return } err = ch.SendRequest(&interfaces.SwInterfaceSetFlags{ - SwIfIndex: 0, - AdminUpDown: 1, + SwIfIndex: index, + Flags: 0, }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{}) if err != nil { - panic(err) + 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()), EnableDisable: 0, }).ReceiveReply(&interfaces.WantInterfaceEventsReply{}) if err != nil { - panic(err) + logError(err, "setting interface flags") + return } // unsubscribe from delivery of the notifications err = sub.Unsubscribe() + if err != nil { + logError(err, "unsubscribing from interface events") + return + } + + fmt.Println("OK") + fmt.Println() +} + +func marshal(v interface{}) { + fmt.Printf("GO: %#v\n", v) + b, err := json.MarshalIndent(v, "", " ") if err != nil { panic(err) } + fmt.Printf("JSON: %s\n", b) +} + +var errors []error + +func logError(err error, msg string) { + fmt.Printf("ERROR: %s: %v\n", msg, err) + errors = append(errors, err) }