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