Change module name to go.fd.io/govpp
[govpp.git] / examples / stream-client / stream_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 // stream-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         "time"
26
27         "go.fd.io/govpp"
28         "go.fd.io/govpp/adapter/socketclient"
29         "go.fd.io/govpp/api"
30         interfaces "go.fd.io/govpp/binapi/interface"
31         "go.fd.io/govpp/binapi/interface_types"
32         "go.fd.io/govpp/binapi/ip"
33         "go.fd.io/govpp/binapi/ip_types"
34         "go.fd.io/govpp/binapi/mactime"
35         "go.fd.io/govpp/binapi/memclnt"
36         "go.fd.io/govpp/binapi/vpe"
37         "go.fd.io/govpp/core"
38 )
39
40 var (
41         sockAddr = flag.String("sock", socketclient.DefaultSocketName, "Path to VPP binary API socket file")
42 )
43
44 func main() {
45         flag.Parse()
46
47         fmt.Println("Starting stream client example")
48         fmt.Println()
49
50         // connect to VPP asynchronously
51         conn, connEv, err := govpp.AsyncConnect(*sockAddr, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval)
52         if err != nil {
53                 log.Fatalln("ERROR:", err)
54         }
55         defer conn.Disconnect()
56
57         // wait for Connected event
58         select {
59         case e := <-connEv:
60                 if e.State != core.Connected {
61                         log.Fatalln("ERROR: connecting to VPP failed:", e.Error)
62                 }
63         }
64
65         // check compatibility of used messages
66         ch, err := conn.NewAPIChannel()
67         if err != nil {
68                 log.Fatalln("ERROR: creating channel failed:", err)
69         }
70         defer ch.Close()
71         if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
72                 log.Fatal(err)
73         }
74         if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
75                 log.Fatal(err)
76         }
77
78         // process errors encountered during the example
79         defer func() {
80                 if len(errors) > 0 {
81                         fmt.Printf("finished with %d errors\n", len(errors))
82                         os.Exit(1)
83                 } else {
84                         fmt.Println("finished successfully")
85                 }
86         }()
87
88         // send and receive messages using stream (low-low level API)
89         stream, err := conn.NewStream(context.Background(),
90                 core.WithRequestSize(50),
91                 core.WithReplySize(50),
92                 core.WithReplyTimeout(2*time.Second))
93         if err != nil {
94                 panic(err)
95         }
96         defer func() {
97                 if err := stream.Close(); err != nil {
98                         logError(err, "closing the stream")
99                 }
100         }()
101         getVppVersionStream(stream)
102         idx := createLoopbackStream(stream)
103         interfaceDumpStream(stream)
104         addIPAddressStream(stream, idx)
105         ipAddressDumpStream(stream, idx)
106         mactimeDump(stream)
107         return
108 }
109
110 func getVppVersionStream(stream api.Stream) {
111         fmt.Println("Retrieving version..")
112
113         req := &vpe.ShowVersion{}
114         if err := stream.SendMsg(req); err != nil {
115                 logError(err, "ShowVersion sending message")
116                 return
117         }
118         recv, err := stream.RecvMsg()
119         if err != nil {
120                 logError(err, "ShowVersion receive message")
121                 return
122         }
123         recvMsg := recv.(*vpe.ShowVersionReply)
124
125         fmt.Printf("Retrieved VPP version: %q\n", recvMsg.Version)
126         fmt.Println("OK")
127         fmt.Println()
128 }
129
130 func createLoopbackStream(stream api.Stream) (ifIdx interface_types.InterfaceIndex) {
131         fmt.Println("Creating the loopback interface..")
132
133         req := &interfaces.CreateLoopback{}
134         if err := stream.SendMsg(req); err != nil {
135                 logError(err, "CreateLoopback sending message")
136                 return
137         }
138         recv, err := stream.RecvMsg()
139         if err != nil {
140                 logError(err, "CreateLoopback receive message")
141                 return
142         }
143         recvMsg := recv.(*interfaces.CreateLoopbackReply)
144
145         fmt.Printf("Loopback interface index: %v\n", recvMsg.SwIfIndex)
146         fmt.Println("OK")
147         fmt.Println()
148
149         return recvMsg.SwIfIndex
150 }
151
152 func interfaceDumpStream(stream api.Stream) {
153         fmt.Println("Dumping interfaces..")
154
155         if err := stream.SendMsg(&interfaces.SwInterfaceDump{
156                 SwIfIndex: ^interface_types.InterfaceIndex(0),
157         }); err != nil {
158                 logError(err, "SwInterfaceDump sending message")
159                 return
160         }
161         if err := stream.SendMsg(&memclnt.ControlPing{}); err != nil {
162                 logError(err, "ControlPing sending message")
163                 return
164         }
165
166 Loop:
167         for {
168                 msg, err := stream.RecvMsg()
169                 if err != nil {
170                         logError(err, "SwInterfaceDump receiving message ")
171                         return
172                 }
173
174                 switch msg.(type) {
175                 case *interfaces.SwInterfaceDetails:
176                         fmt.Printf(" - SwInterfaceDetails: %+v\n", msg)
177
178                 case *memclnt.ControlPingReply:
179                         fmt.Printf(" - ControlPingReply: %+v\n", msg)
180                         break Loop
181
182                 default:
183                         logError(err, "unexpected message")
184                         return
185                 }
186         }
187
188         fmt.Println("OK")
189         fmt.Println()
190 }
191
192 func addIPAddressStream(stream api.Stream, index interface_types.InterfaceIndex) {
193         fmt.Printf("Adding IP address to the interface index %d..\n", index)
194
195         if err := stream.SendMsg(&interfaces.SwInterfaceAddDelAddress{
196                 SwIfIndex: index,
197                 IsAdd:     true,
198                 Prefix: ip_types.AddressWithPrefix{
199                         Address: ip_types.Address{
200                                 Af: ip_types.ADDRESS_IP4,
201                                 Un: ip_types.AddressUnionIP4(ip_types.IP4Address{10, 10, 0, uint8(index)}),
202                         },
203                         Len: 32,
204                 },
205         }); err != nil {
206                 logError(err, "SwInterfaceAddDelAddress sending message")
207                 return
208         }
209
210         recv, err := stream.RecvMsg()
211         if err != nil {
212                 logError(err, "SwInterfaceAddDelAddressReply receiving message")
213                 return
214         }
215         recvMsg := recv.(*interfaces.SwInterfaceAddDelAddressReply)
216
217         fmt.Printf("Added IP address to interface: %v (return value: %d)\n", index, recvMsg.Retval)
218         fmt.Println("OK")
219         fmt.Println()
220 }
221
222 func ipAddressDumpStream(stream api.Stream, index interface_types.InterfaceIndex) {
223         fmt.Printf("Dumping IP addresses for interface index %d..\n", index)
224
225         if err := stream.SendMsg(&ip.IPAddressDump{
226                 SwIfIndex: index,
227         }); err != nil {
228                 logError(err, "IPAddressDump sending message")
229                 return
230         }
231         if err := stream.SendMsg(&memclnt.ControlPing{}); err != nil {
232                 logError(err, "ControlPing sending sending message")
233                 return
234         }
235
236 Loop:
237         for {
238                 msg, err := stream.RecvMsg()
239                 if err != nil {
240                         logError(err, "IPAddressDump receiving message ")
241                         return
242                 }
243
244                 switch msg.(type) {
245                 case *ip.IPAddressDetails:
246                         fmt.Printf(" - IPAddressDetails: %+v\n", msg)
247
248                 case *memclnt.ControlPingReply:
249                         fmt.Printf(" - ControlPingReply: %+v\n", msg)
250                         break Loop
251
252                 default:
253                         logError(err, "unexpected message")
254                         return
255                 }
256         }
257
258         fmt.Println("OK")
259         fmt.Println()
260 }
261
262 // Mactime dump uses MactimeDumpReply message as an end of the stream
263 // notification instead of the control ping.
264 func mactimeDump(stream api.Stream) {
265         fmt.Println("Sending mactime dump..")
266
267         if err := stream.SendMsg(&mactime.MactimeDump{}); err != nil {
268                 logError(err, "sending mactime dump")
269                 return
270         }
271
272 Loop:
273         for {
274                 msg, err := stream.RecvMsg()
275                 if err != nil {
276                         logError(err, "MactimeDump receiving message")
277                         return
278                 }
279
280                 switch msg.(type) {
281                 case *mactime.MactimeDetails:
282                         fmt.Printf(" - MactimeDetails: %+v\n", msg)
283
284                 case *mactime.MactimeDumpReply:
285                         fmt.Printf(" - MactimeDumpReply: %+v\n", msg)
286                         break Loop
287
288                 default:
289                         logError(err, "unexpected message")
290                         return
291                 }
292         }
293
294         fmt.Println("OK")
295         fmt.Println()
296 }
297
298 var errors []error
299
300 func logError(err error, msg string) {
301         fmt.Printf("ERROR: %s: %v\n", msg, err)
302         errors = append(errors, err)
303 }