Improve compatibility checking
[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         vppVersion(ch)
67
68         if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
69                 log.Fatal(err)
70         }
71
72         createLoopback(ch)
73         createLoopback(ch)
74         interfaceDump(ch)
75
76         addIPAddress(ch)
77         ipAddressDump(ch)
78
79         interfaceNotifications(ch)
80
81         if len(Errors) > 0 {
82                 fmt.Printf("finished with %d errors\n", len(Errors))
83                 os.Exit(1)
84         } else {
85                 fmt.Println("finished successfully")
86         }
87 }
88
89 var Errors []error
90
91 func logError(err error, msg string) {
92         fmt.Printf("ERROR: %s: %v\n", msg, err)
93         Errors = append(Errors, err)
94 }
95
96 // vppVersion is the simplest API example - it retrieves VPP version.
97 func vppVersion(ch api.Channel) {
98         fmt.Println("Retrieving version")
99
100         req := &vpe.ShowVersion{}
101         reply := &vpe.ShowVersionReply{}
102
103         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
104                 logError(err, "retrieving version")
105                 return
106         }
107         fmt.Printf("reply: %+v\n", reply)
108
109         fmt.Printf("VPP version: %q\n", cleanString(reply.Version))
110         fmt.Println("ok")
111 }
112
113 // createLoopback sends request to create loopback interface.
114 func createLoopback(ch api.Channel) {
115         fmt.Println("Creating loopback interface")
116
117         req := &interfaces.CreateLoopback{}
118         reply := &interfaces.CreateLoopbackReply{}
119
120         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
121                 logError(err, "creating loopback interface")
122                 return
123         }
124         fmt.Printf("reply: %+v\n", reply)
125
126         fmt.Printf("loopback interface index: %v\n", reply.SwIfIndex)
127         fmt.Println("OK")
128 }
129
130 // interfaceDump shows an example of multipart request (multiple replies are expected).
131 func interfaceDump(ch api.Channel) {
132         fmt.Println("Dumping interfaces")
133
134         reqCtx := ch.SendMultiRequest(&interfaces.SwInterfaceDump{})
135         for {
136                 msg := &interfaces.SwInterfaceDetails{}
137                 stop, err := reqCtx.ReceiveReply(msg)
138                 if err != nil {
139                         logError(err, "dumping interfaces")
140                         return
141                 }
142                 if stop {
143                         break
144                 }
145                 fmt.Printf(" - interface: %+v\n", msg)
146         }
147
148         fmt.Println("OK")
149 }
150
151 // addIPAddress sends request to add IP address to interface.
152 func addIPAddress(ch api.Channel) {
153         fmt.Println("Adding IP address to interface")
154
155         req := &interfaces.SwInterfaceAddDelAddress{
156                 SwIfIndex:     1,
157                 IsAdd:         1,
158                 Address:       []byte{10, 10, 0, 1},
159                 AddressLength: 24,
160                 /* below for 20.01-rc0
161                 IsAdd:     true,
162                 Prefix: interfaces.Prefix{
163                         Address: interfaces.Address{
164                                 Af: interfaces.ADDRESS_IP4,
165                                 Un: interfaces.AddressUnionIP4(interfaces.IP4Address{10, 10, 0, 1}),
166                         },
167                         Len: 24,
168                 },*/
169         }
170         reply := &interfaces.SwInterfaceAddDelAddressReply{}
171
172         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
173                 logError(err, "adding IP address to interface")
174                 return
175         }
176         fmt.Printf("reply: %+v\n", reply)
177
178         fmt.Println("OK")
179 }
180
181 func ipAddressDump(ch api.Channel) {
182         fmt.Println("Dumping IP addresses")
183
184         req := &ip.IPAddressDump{
185                 SwIfIndex: 1,
186         }
187         reqCtx := ch.SendMultiRequest(req)
188
189         for {
190                 msg := &ip.IPAddressDetails{}
191                 stop, err := reqCtx.ReceiveReply(msg)
192                 if err != nil {
193                         logError(err, "dumping IP addresses")
194                         return
195                 }
196                 if stop {
197                         break
198                 }
199                 fmt.Printf(" - ip address: %+v\n", msg)
200         }
201
202         fmt.Println("OK")
203 }
204
205 // interfaceNotifications shows the usage of notification API. Note that for notifications,
206 // you are supposed to create your own Go channel with your preferred buffer size. If the channel's
207 // buffer is full, the notifications will not be delivered into it.
208 func interfaceNotifications(ch api.Channel) {
209         fmt.Println("Subscribing to notificaiton events")
210
211         notifChan := make(chan api.Message, 100)
212
213         // subscribe for specific notification message
214         sub, err := ch.SubscribeNotification(notifChan, &interfaces.SwInterfaceEvent{})
215         if err != nil {
216                 logError(err, "subscribing to interface events")
217                 return
218         }
219
220         // enable interface events in VPP
221         err = ch.SendRequest(&interfaces.WantInterfaceEvents{
222                 PID:           uint32(os.Getpid()),
223                 EnableDisable: 1,
224         }).ReceiveReply(&interfaces.WantInterfaceEventsReply{})
225         if err != nil {
226                 logError(err, "enabling interface events")
227                 return
228         }
229
230         // generate some events in VPP
231         err = ch.SendRequest(&interfaces.SwInterfaceSetFlags{
232                 SwIfIndex: 1,
233         }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{})
234         if err != nil {
235                 logError(err, "setting interface flags")
236                 return
237         }
238         err = ch.SendRequest(&interfaces.SwInterfaceSetFlags{
239                 SwIfIndex:   1,
240                 AdminUpDown: 1,
241                 /* below for 20.01-rc0
242                 AdminUpDown: true,
243                 Flags:     interfaces.IF_STATUS_API_FLAG_ADMIN_UP,*/
244         }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{})
245         if err != nil {
246                 logError(err, "setting interface flags")
247                 return
248         }
249
250         // receive one notification
251         notif := (<-notifChan).(*interfaces.SwInterfaceEvent)
252         fmt.Printf("incoming event: %+v\n", notif)
253
254         // disable interface events in VPP
255         err = ch.SendRequest(&interfaces.WantInterfaceEvents{
256                 PID:           uint32(os.Getpid()),
257                 EnableDisable: 0,
258         }).ReceiveReply(&interfaces.WantInterfaceEventsReply{})
259         if err != nil {
260                 logError(err, "setting interface flags")
261                 return
262         }
263
264         // unsubscribe from delivery of the notifications
265         err = sub.Unsubscribe()
266         if err != nil {
267                 logError(err, "unsubscribing from interface events")
268                 return
269         }
270
271         fmt.Println()
272 }
273
274 func cleanString(str string) string {
275         return strings.Split(str, "\x00")[0]
276 }