Binary API generator improvements
[govpp.git] / examples / simple-client / simple_client.go
index 6429c35..7aeaa0b 100644 (file)
 package main
 
 import (
+       "context"
+       "flag"
        "fmt"
        "log"
-       "net"
        "os"
-       "strings"
 
        "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/bin_api/acl"
-       "git.fd.io/govpp.git/examples/bin_api/interfaces"
-       "git.fd.io/govpp.git/examples/bin_api/ip"
+       "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/mactime"
+       "git.fd.io/govpp.git/examples/binapi/vpe"
+)
+
+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")
 
-       // connect to VPP
-       conn, conev, err := govpp.AsyncConnect("")
+       // connect to VPP asynchronously
+       conn, conev, 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 := <-conev:
                if e.State != core.Connected {
-                       log.Fatalf("failed to connect: %v", e.Error)
+                       log.Fatalln("ERROR: connecting to VPP failed:", e.Error)
                }
        }
 
        // create an API channel that will be used in the examples
        ch, err := conn.NewAPIChannel()
        if err != nil {
-               log.Fatalln("ERROR:", err)
+               log.Fatalln("ERROR: creating channel failed:", err)
        }
        defer ch.Close()
 
-       // individual examples
-       aclVersion(ch)
-       aclConfig(ch)
-       aclDump(ch)
+       if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
+               log.Fatal(err)
+       }
 
-       interfaceDump(ch)
-       ipAddressDump(ch)
+       vppVersion(ch)
 
-       setIpUnnumbered(ch)
-       ipUnnumberedDump(ch)
+       if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
+               log.Fatal(err)
+       }
 
-       interfaceNotifications(ch)
-}
+       idx := createLoopback(ch)
+       interfaceDump(ch)
 
-// aclVersion is the simplest API example - one empty request message and one reply message.
-func aclVersion(ch api.Channel) {
-       fmt.Println("ACL getting version")
+       addIPAddress(ch, idx)
+       ipAddressDump(ch, idx)
+       interfaceNotifications(ch, idx)
 
-       req := &acl.ACLPluginGetVersion{}
-       reply := &acl.ACLPluginGetVersionReply{}
+       mactimeDump(conn)
 
-       if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
-               fmt.Println("ERROR:", err)
+       if len(Errors) > 0 {
+               fmt.Printf("finished with %d errors\n", len(Errors))
+               os.Exit(1)
        } else {
-               fmt.Printf("ACL version reply: %+v\n", reply)
+               fmt.Println("finished successfully")
        }
 }
 
-// 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{}
+var Errors []error
+
+func logError(err error, msg string) {
+       fmt.Printf("ERROR: %s: %v\n", msg, err)
+       Errors = append(Errors, err)
+}
+
+// vppVersion is the simplest API example - it retrieves VPP version.
+func vppVersion(ch api.Channel) {
+       fmt.Println("Retrieving version")
+
+       req := &vpe.ShowVersion{}
+       reply := &vpe.ShowVersionReply{}
 
        if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
-               fmt.Println("ERROR:", err)
+               logError(err, "retrieving version")
                return
        }
-       if reply.Retval != 0 {
-               fmt.Println("Retval:", reply.Retval)
-               return
-       }
-
-       fmt.Printf("ACL add replace reply: %+v\n", reply)
+       fmt.Printf("reply: %+v\n", reply)
 
+       fmt.Printf("VPP version: %q\n", reply.Version)
+       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")
+// createLoopback sends request to create loopback interface.
+func createLoopback(ch api.Channel) interface_types.InterfaceIndex {
+       fmt.Println("Creating loopback interface")
 
-       req := &acl.ACLDump{}
-       reply := &acl.ACLDetails{}
+       req := &interfaces.CreateLoopback{}
+       reply := &interfaces.CreateLoopbackReply{}
 
-       reqCtx := ch.SendRequest(req)
-
-       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("reply: %+v\n", reply)
+
+       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)
@@ -153,84 +151,82 @@ 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)
+               n++
+               fmt.Printf(" - interface #%d: %+v\n", n, msg)
        }
-}
-
-func ipAddressDump(ch api.Channel) {
-       fmt.Println("Dumping IP addresses")
 
-       req := &ip.IPAddressDump{
-               SwIfIndex: 1, //^uint32(0),
-       }
-       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,
+// addIPAddress sends request to add IP address to interface.
+func addIPAddress(ch api.Channel, index interface_types.InterfaceIndex) {
+       fmt.Printf("Adding IP address to interface 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{}
+       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.Printf("reply: %+v\n", reply)
+
+       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)
        }
+
+       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
@@ -239,41 +235,94 @@ 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 {
+                       fmt.Printf("incoming event: %+v\n", notif.(*interfaces.SwInterfaceEvent))
+               }
+       }()
+
        // 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 mactimeDump(conn api.Connection) {
+       fmt.Println("Sending mactime dump")
+
+       ctx := context.Background()
+
+       stream, err := conn.NewStream(ctx)
        if err != nil {
                panic(err)
        }
+       defer stream.Close()
+
+       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, "dumping mactime")
+                       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()
 }