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