Fix binapigen decoding and minor improvements
[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                 SwIfIndex: ^interface_types.InterfaceIndex(0),
149         })
150         for {
151                 msg := &interfaces.SwInterfaceDetails{}
152                 stop, err := reqCtx.ReceiveReply(msg)
153                 if stop {
154                         break
155                 }
156                 if err != nil {
157                         logError(err, "dumping interfaces")
158                         return
159                 }
160                 n++
161                 fmt.Printf(" - interface #%d: %+v\n", n, msg)
162                 marshal(msg)
163         }
164
165         fmt.Println("OK")
166         fmt.Println()
167 }
168
169 // addIPAddress sends request to add IP address to interface.
170 func addIPAddress(ch api.Channel, index interface_types.InterfaceIndex) {
171         fmt.Printf("Adding IP address to interface to interface index %d\n", index)
172
173         req := &interfaces.SwInterfaceAddDelAddress{
174                 SwIfIndex: index,
175                 IsAdd:     true,
176                 Prefix: ip_types.AddressWithPrefix{
177                         Address: ip_types.Address{
178                                 Af: ip_types.ADDRESS_IP4,
179                                 Un: ip_types.AddressUnionIP4(ip_types.IP4Address{10, 10, 0, uint8(index)}),
180                         },
181                         Len: 32,
182                 },
183         }
184         marshal(req)
185         reply := &interfaces.SwInterfaceAddDelAddressReply{}
186
187         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
188                 logError(err, "adding IP address to interface")
189                 return
190         }
191         fmt.Printf("reply: %+v\n", reply)
192
193         fmt.Println("OK")
194         fmt.Println()
195 }
196
197 func ipAddressDump(ch api.Channel, index interface_types.InterfaceIndex) {
198         fmt.Printf("Dumping IP addresses for interface index %d\n", index)
199
200         req := &ip.IPAddressDump{
201                 SwIfIndex: index,
202         }
203         reqCtx := ch.SendMultiRequest(req)
204
205         for {
206                 msg := &ip.IPAddressDetails{}
207                 stop, err := reqCtx.ReceiveReply(msg)
208                 if err != nil {
209                         logError(err, "dumping IP addresses")
210                         return
211                 }
212                 if stop {
213                         break
214                 }
215                 fmt.Printf(" - ip address: %+v\n", msg)
216                 marshal(msg)
217         }
218
219         fmt.Println("OK")
220         fmt.Println()
221 }
222
223 // interfaceNotifications shows the usage of notification API. Note that for notifications,
224 // you are supposed to create your own Go channel with your preferred buffer size. If the channel's
225 // buffer is full, the notifications will not be delivered into it.
226 func interfaceNotifications(ch api.Channel, index interface_types.InterfaceIndex) {
227         fmt.Printf("Subscribing to notificaiton events for interface index %d\n", index)
228
229         notifChan := make(chan api.Message, 100)
230
231         // subscribe for specific notification message
232         sub, err := ch.SubscribeNotification(notifChan, &interfaces.SwInterfaceEvent{})
233         if err != nil {
234                 logError(err, "subscribing to interface events")
235                 return
236         }
237
238         // enable interface events in VPP
239         err = ch.SendRequest(&interfaces.WantInterfaceEvents{
240                 PID:           uint32(os.Getpid()),
241                 EnableDisable: 1,
242         }).ReceiveReply(&interfaces.WantInterfaceEventsReply{})
243         if err != nil {
244                 logError(err, "enabling interface events")
245                 return
246         }
247
248         // receive notifications
249         go func() {
250                 for notif := range notifChan {
251                         e := notif.(*interfaces.SwInterfaceEvent)
252                         fmt.Printf("incoming event: %+v\n", e)
253                         marshal(e)
254                 }
255         }()
256
257         // generate some events in VPP
258         err = ch.SendRequest(&interfaces.SwInterfaceSetFlags{
259                 SwIfIndex: index,
260                 Flags:     interface_types.IF_STATUS_API_FLAG_ADMIN_UP,
261         }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{})
262         if err != nil {
263                 logError(err, "setting interface flags")
264                 return
265         }
266         err = ch.SendRequest(&interfaces.SwInterfaceSetFlags{
267                 SwIfIndex: index,
268                 Flags:     0,
269         }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{})
270         if err != nil {
271                 logError(err, "setting interface flags")
272                 return
273         }
274
275         // disable interface events in VPP
276         err = ch.SendRequest(&interfaces.WantInterfaceEvents{
277                 PID:           uint32(os.Getpid()),
278                 EnableDisable: 0,
279         }).ReceiveReply(&interfaces.WantInterfaceEventsReply{})
280         if err != nil {
281                 logError(err, "setting interface flags")
282                 return
283         }
284
285         // unsubscribe from delivery of the notifications
286         err = sub.Unsubscribe()
287         if err != nil {
288                 logError(err, "unsubscribing from interface events")
289                 return
290         }
291
292         fmt.Println("OK")
293         fmt.Println()
294 }
295
296 func mactimeDump(conn api.Connection) {
297         fmt.Println("Sending mactime dump")
298
299         ctx := context.Background()
300
301         stream, err := conn.NewStream(ctx)
302         if err != nil {
303                 panic(err)
304         }
305         defer stream.Close()
306
307         if err := stream.SendMsg(&mactime.MactimeDump{}); err != nil {
308                 logError(err, "sending mactime dump")
309                 return
310         }
311
312 Loop:
313         for {
314                 msg, err := stream.RecvMsg()
315                 if err != nil {
316                         logError(err, "dumping mactime")
317                         return
318                 }
319
320                 switch msg.(type) {
321                 case *mactime.MactimeDetails:
322                         fmt.Printf(" - MactimeDetails: %+v\n", msg)
323
324                 case *mactime.MactimeDumpReply:
325                         fmt.Printf(" - MactimeDumpReply: %+v\n", msg)
326                         break Loop
327
328                 default:
329                         logError(err, "unexpected message")
330                         return
331                 }
332         }
333
334         fmt.Println("OK")
335         fmt.Println()
336 }
337
338 func marshal(v interface{}) {
339         fmt.Printf("GO: %#v\n", v)
340         b, err := json.MarshalIndent(v, "", "  ")
341         if err != nil {
342                 panic(err)
343         }
344         fmt.Printf("JSON: %s\n", b)
345 }