Improve binapi generator
[govpp.git] / examples / simple-client / simple_client.go
1 // Copyright (c) 2017 Cisco and/or its affiliates.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // simple-client is an example VPP management application that exercises the
16 // govpp API on real-world use-cases.
17 package main
18
19 import (
20         "context"
21         "encoding/json"
22         "flag"
23         "fmt"
24         "log"
25         "os"
26
27         "git.fd.io/govpp.git"
28         "git.fd.io/govpp.git/adapter/socketclient"
29         "git.fd.io/govpp.git/api"
30         interfaces "git.fd.io/govpp.git/binapi/interface"
31         "git.fd.io/govpp.git/binapi/interface_types"
32         "git.fd.io/govpp.git/binapi/ip"
33         "git.fd.io/govpp.git/binapi/ip_types"
34         "git.fd.io/govpp.git/binapi/mactime"
35         "git.fd.io/govpp.git/binapi/vpe"
36         "git.fd.io/govpp.git/core"
37 )
38
39 var (
40         sockAddr = flag.String("sock", socketclient.DefaultSocketName, "Path to VPP binary API socket file")
41 )
42
43 func main() {
44         flag.Parse()
45
46         fmt.Println("Starting simple client example")
47
48         // connect to VPP asynchronously
49         conn, conev, err := govpp.AsyncConnect(*sockAddr, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
50         if err != nil {
51                 log.Fatalln("ERROR:", err)
52         }
53         defer conn.Disconnect()
54
55         // wait for Connected event
56         select {
57         case e := <-conev:
58                 if e.State != core.Connected {
59                         log.Fatalln("ERROR: connecting to VPP failed:", e.Error)
60                 }
61         }
62
63         // create an API channel that will be used in the examples
64         ch, err := conn.NewAPIChannel()
65         if err != nil {
66                 log.Fatalln("ERROR: creating channel failed:", err)
67         }
68         defer ch.Close()
69
70         if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
71                 log.Fatal(err)
72         }
73
74         vppVersion(ch)
75
76         if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
77                 log.Fatal(err)
78         }
79
80         idx := createLoopback(ch)
81         interfaceDump(ch)
82
83         addIPAddress(ch, idx)
84         ipAddressDump(ch, idx)
85         interfaceNotifications(ch, idx)
86
87         mactimeDump(conn)
88
89         if len(Errors) > 0 {
90                 fmt.Printf("finished with %d errors\n", len(Errors))
91                 os.Exit(1)
92         } else {
93                 fmt.Println("finished successfully")
94         }
95 }
96
97 var Errors []error
98
99 func logError(err error, msg string) {
100         fmt.Printf("ERROR: %s: %v\n", msg, err)
101         Errors = append(Errors, err)
102 }
103
104 // vppVersion is the simplest API example - it retrieves VPP version.
105 func vppVersion(ch api.Channel) {
106         fmt.Println("Retrieving version")
107
108         req := &vpe.ShowVersion{}
109         reply := &vpe.ShowVersionReply{}
110
111         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
112                 logError(err, "retrieving version")
113                 return
114         }
115         fmt.Printf("reply: %+v\n", reply)
116
117         fmt.Printf("VPP version: %q\n", reply.Version)
118         fmt.Println("OK")
119         fmt.Println()
120 }
121
122 // createLoopback sends request to create loopback interface.
123 func createLoopback(ch api.Channel) interface_types.InterfaceIndex {
124         fmt.Println("Creating loopback interface")
125
126         req := &interfaces.CreateLoopback{}
127         reply := &interfaces.CreateLoopbackReply{}
128
129         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
130                 logError(err, "creating loopback interface")
131                 return 0
132         }
133         fmt.Printf("reply: %+v\n", reply)
134
135         fmt.Printf("interface index: %v\n", reply.SwIfIndex)
136         fmt.Println("OK")
137         fmt.Println()
138
139         return reply.SwIfIndex
140 }
141
142 // interfaceDump shows an example of multipart request (multiple replies are expected).
143 func interfaceDump(ch api.Channel) {
144         fmt.Println("Dumping interfaces")
145
146         n := 0
147         reqCtx := ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
148         for {
149                 msg := &interfaces.SwInterfaceDetails{}
150                 stop, err := reqCtx.ReceiveReply(msg)
151                 if stop {
152                         break
153                 }
154                 if err != nil {
155                         logError(err, "dumping interfaces")
156                         return
157                 }
158                 n++
159                 fmt.Printf(" - interface #%d: %+v\n", n, msg)
160                 marshal(msg)
161         }
162
163         fmt.Println("OK")
164         fmt.Println()
165 }
166
167 // addIPAddress sends request to add IP address to interface.
168 func addIPAddress(ch api.Channel, index interface_types.InterfaceIndex) {
169         fmt.Printf("Adding IP address to interface to interface index %d\n", index)
170
171         req := &interfaces.SwInterfaceAddDelAddress{
172                 SwIfIndex: index,
173                 IsAdd:     true,
174                 Prefix: ip_types.AddressWithPrefix{
175                         Address: ip_types.Address{
176                                 Af: ip_types.ADDRESS_IP4,
177                                 Un: ip_types.AddressUnionIP4(ip_types.IP4Address{10, 10, 0, uint8(index)}),
178                         },
179                         Len: 32,
180                 },
181         }
182         marshal(req)
183         reply := &interfaces.SwInterfaceAddDelAddressReply{}
184
185         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
186                 logError(err, "adding IP address to interface")
187                 return
188         }
189         fmt.Printf("reply: %+v\n", reply)
190
191         fmt.Println("OK")
192         fmt.Println()
193 }
194
195 func ipAddressDump(ch api.Channel, index interface_types.InterfaceIndex) {
196         fmt.Printf("Dumping IP addresses for interface index %d\n", index)
197
198         req := &ip.IPAddressDump{
199                 SwIfIndex: index,
200         }
201         reqCtx := ch.SendMultiRequest(req)
202
203         for {
204                 msg := &ip.IPAddressDetails{}
205                 stop, err := reqCtx.ReceiveReply(msg)
206                 if err != nil {
207                         logError(err, "dumping IP addresses")
208                         return
209                 }
210                 if stop {
211                         break
212                 }
213                 fmt.Printf(" - ip address: %+v\n", msg)
214                 marshal(msg)
215         }
216
217         fmt.Println("OK")
218         fmt.Println()
219 }
220
221 // interfaceNotifications shows the usage of notification API. Note that for notifications,
222 // you are supposed to create your own Go channel with your preferred buffer size. If the channel's
223 // buffer is full, the notifications will not be delivered into it.
224 func interfaceNotifications(ch api.Channel, index interface_types.InterfaceIndex) {
225         fmt.Printf("Subscribing to notificaiton events for interface index %d\n", index)
226
227         notifChan := make(chan api.Message, 100)
228
229         // subscribe for specific notification message
230         sub, err := ch.SubscribeNotification(notifChan, &interfaces.SwInterfaceEvent{})
231         if err != nil {
232                 logError(err, "subscribing to interface events")
233                 return
234         }
235
236         // enable interface events in VPP
237         err = ch.SendRequest(&interfaces.WantInterfaceEvents{
238                 PID:           uint32(os.Getpid()),
239                 EnableDisable: 1,
240         }).ReceiveReply(&interfaces.WantInterfaceEventsReply{})
241         if err != nil {
242                 logError(err, "enabling interface events")
243                 return
244         }
245
246         // receive notifications
247         go func() {
248                 for notif := range notifChan {
249                         e := notif.(*interfaces.SwInterfaceEvent)
250                         fmt.Printf("incoming event: %+v\n", e)
251                         marshal(e)
252                 }
253         }()
254
255         // generate some events in VPP
256         err = ch.SendRequest(&interfaces.SwInterfaceSetFlags{
257                 SwIfIndex: index,
258                 Flags:     interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
259         }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{})
260         if err != nil {
261                 logError(err, "setting interface flags")
262                 return
263         }
264         err = ch.SendRequest(&interfaces.SwInterfaceSetFlags{
265                 SwIfIndex: index,
266                 Flags:     0,
267         }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{})
268         if err != nil {
269                 logError(err, "setting interface flags")
270                 return
271         }
272
273         // disable interface events in VPP
274         err = ch.SendRequest(&interfaces.WantInterfaceEvents{
275                 PID:           uint32(os.Getpid()),
276                 EnableDisable: 0,
277         }).ReceiveReply(&interfaces.WantInterfaceEventsReply{})
278         if err != nil {
279                 logError(err, "setting interface flags")
280                 return
281         }
282
283         // unsubscribe from delivery of the notifications
284         err = sub.Unsubscribe()
285         if err != nil {
286                 logError(err, "unsubscribing from interface events")
287                 return
288         }
289
290         fmt.Println("OK")
291         fmt.Println()
292 }
293
294 func mactimeDump(conn api.Connection) {
295         fmt.Println("Sending mactime dump")
296
297         ctx := context.Background()
298
299         stream, err := conn.NewStream(ctx)
300         if err != nil {
301                 panic(err)
302         }
303         defer stream.Close()
304
305         if err := stream.SendMsg(&mactime.MactimeDump{}); err != nil {
306                 logError(err, "sending mactime dump")
307                 return
308         }
309
310 Loop:
311         for {
312                 msg, err := stream.RecvMsg()
313                 if err != nil {
314                         logError(err, "dumping mactime")
315                         return
316                 }
317
318                 switch msg.(type) {
319                 case *mactime.MactimeDetails:
320                         fmt.Printf(" - MactimeDetails: %+v\n", msg)
321
322                 case *mactime.MactimeDumpReply:
323                         fmt.Printf(" - MactimeDumpReply: %+v\n", msg)
324                         break Loop
325
326                 default:
327                         logError(err, "unexpected message")
328                         return
329                 }
330         }
331
332         fmt.Println("OK")
333         fmt.Println()
334 }
335
336 func marshal(v interface{}) {
337         fmt.Printf("GO: %#v\n", v)
338         b, err := json.MarshalIndent(v, "", "  ")
339         if err != nil {
340                 panic(err)
341         }
342         fmt.Printf("JSON: %s\n", b)
343 }