1978c170f40eed241beb32fffa97f38755b72106
[govpp.git] / examples / api-trace / api-trace.go
1 // Copyright (c) 2021 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 // api-trace is and example how to use the GoVPP API trace tool.
16
17 package main
18
19 import (
20         "context"
21         "flag"
22         "fmt"
23         "git.fd.io/govpp.git"
24         "git.fd.io/govpp.git/adapter/socketclient"
25         "git.fd.io/govpp.git/api"
26         interfaces "git.fd.io/govpp.git/binapi/interface"
27         "git.fd.io/govpp.git/binapi/interface_types"
28         "git.fd.io/govpp.git/binapi/ip_types"
29         "git.fd.io/govpp.git/binapi/memclnt"
30         "git.fd.io/govpp.git/binapi/vpe"
31         "git.fd.io/govpp.git/core"
32         "log"
33 )
34
35 var (
36         sockAddr = flag.String("socket", socketclient.DefaultSocketName, "Path to VPP API socket file")
37 )
38
39 func main() {
40         flag.Parse()
41
42         fmt.Printf("Starting api-trace tool example\n\n")
43
44         // make synchronous VPP connection
45         conn, err := govpp.Connect(*sockAddr)
46         if err != nil {
47                 log.Fatalln("ERROR:", err)
48         }
49         defer conn.Disconnect()
50
51         singleChannel(conn)
52         multiChannel(conn)
53         stream(conn)
54
55         fmt.Printf("Api-trace tool example finished\n\n")
56 }
57
58 func singleChannel(conn *core.Connection) {
59         // create new channel and perform simple compatibility check
60         ch, err := conn.NewAPIChannel()
61         if err != nil {
62                 log.Fatalln("ERROR: creating channel failed:", err)
63         }
64         defer ch.Close()
65
66         fmt.Printf("=> Example 1\n\nEnabling API trace...\n")
67         conn.Trace().Enable(true)
68
69         if err := ch.CheckCompatiblity(append(vpe.AllMessages(), interfaces.AllMessages()...)...); err != nil {
70                 log.Fatal(err)
71         }
72
73         // do some API calls
74         fmt.Printf("Calling VPP API...\n")
75         retrieveVersion(ch)
76         idx := createLoopback(ch)
77         addIPAddress("10.10.0.1/24", ch, idx)
78         interfaceDump(ch)
79         deleteLoopback(ch, idx)
80         fmt.Println()
81
82         fmt.Printf("API trace (api calls: %d):\n", len(conn.Trace().GetRecords()))
83         fmt.Printf("--------------------\n")
84         for _, item := range conn.Trace().GetRecords() {
85                 printTrace(item)
86         }
87         fmt.Printf("--------------------\n")
88
89         fmt.Printf("Clearing API trace...\n\n")
90         conn.Trace().Clear()
91 }
92
93 func multiChannel(conn *core.Connection) {
94         ch1, err := conn.NewAPIChannel()
95         if err != nil {
96                 log.Fatalln("ERROR: creating channel failed:", err)
97         }
98         defer ch1.Close()
99         ch2, err := conn.NewAPIChannel()
100         if err != nil {
101                 log.Fatalln("ERROR: creating channel failed:", err)
102         }
103         defer ch2.Close()
104
105         //do API calls again
106         fmt.Printf("=> Example 2\n\nCalling VPP API (multi-channel)...\n")
107         retrieveVersion(ch1)
108         idx1 := createLoopback(ch1)
109         idx2 := createLoopback(ch2)
110         addIPAddress("20.10.0.1/24", ch1, idx1)
111         addIPAddress("30.10.0.1/24", ch2, idx2)
112         interfaceDump(ch1)
113         deleteLoopback(ch2, idx1)
114         deleteLoopback(ch1, idx2)
115         fmt.Println()
116
117         chan1, ok := ch1.(*core.Channel)
118         if !ok {
119                 log.Fatalln("ERROR: incorrect type of channel 1:", err)
120         }
121         chan2, ok := ch2.(*core.Channel)
122         if !ok {
123                 log.Fatalln("ERROR: incorrect type of channel 2:", err)
124         }
125
126         fmt.Printf("API trace for channel 1 (api calls: %d):\n", len(conn.Trace().GetRecordsForChannel(chan1.GetID())))
127         fmt.Printf("--------------------\n")
128         for _, item := range conn.Trace().GetRecordsForChannel(chan1.GetID()) {
129                 printTrace(item)
130         }
131         fmt.Printf("--------------------\n")
132         fmt.Printf("API trace for channel 2 (api calls: %d):\n", len(conn.Trace().GetRecordsForChannel(chan2.GetID())))
133         fmt.Printf("--------------------\n")
134         for _, item := range conn.Trace().GetRecordsForChannel(chan2.GetID()) {
135                 printTrace(item)
136         }
137         fmt.Printf("--------------------\n")
138         fmt.Printf("cumulative API trace (api calls: %d):\n", len(conn.Trace().GetRecords()))
139         fmt.Printf("--------------------\n")
140         for _, item := range conn.Trace().GetRecords() {
141                 printTrace(item)
142         }
143         fmt.Printf("--------------------\n")
144
145         fmt.Printf("Clearing API trace...\n\n")
146         conn.Trace().Clear()
147 }
148
149 func stream(conn *core.Connection) {
150         // create new channel and perform simple compatibility check
151         s, err := conn.NewStream(context.Background())
152         if err != nil {
153                 log.Fatalln("ERROR: creating channel failed:", err)
154         }
155         defer func() {
156                 if err := s.Close(); err != nil {
157                         log.Fatalf("failed to close stream: %v", err)
158                 }
159         }()
160
161         // do some API calls
162         fmt.Printf("=> Example 3\n\nCalling VPP API (stream)...\n")
163         invokeRetrieveVersion(conn)
164         idx := invokeCreateLoopback(conn)
165         invokeAddIPAddress("40.10.0.1/24", conn, idx)
166         invokeInterfaceDump(conn)
167         invokeDeleteLoopback(conn, idx)
168         fmt.Println()
169
170         fmt.Printf("stream API trace (api calls: %d):\n", len(conn.Trace().GetRecords()))
171         fmt.Printf("--------------------\n")
172         for _, item := range conn.Trace().GetRecords() {
173                 printTrace(item)
174         }
175         fmt.Printf("--------------------\n")
176
177         fmt.Printf("Clearing API trace...\n\n")
178         conn.Trace().GetRecords()
179 }
180
181 func retrieveVersion(ch api.Channel) {
182         req := &vpe.ShowVersion{}
183         reply := &vpe.ShowVersionReply{}
184
185         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
186                 fmt.Printf("ERROR: %v\n", err)
187                 return
188         }
189         fmt.Printf(" - retrieved VPP version: %s\n", reply.Version)
190 }
191
192 func invokeRetrieveVersion(c api.Connection) {
193         req := &vpe.ShowVersion{}
194         reply := &vpe.ShowVersionReply{}
195
196         if err := c.Invoke(context.Background(), req, reply); err != nil {
197                 fmt.Printf("ERROR: %v\n", err)
198         }
199         fmt.Printf(" - retrieved VPP version: %s\n", reply.Version)
200 }
201
202 func createLoopback(ch api.Channel) interface_types.InterfaceIndex {
203         req := &interfaces.CreateLoopback{}
204         reply := &interfaces.CreateLoopbackReply{}
205
206         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
207                 fmt.Printf("ERROR: %v\n", err)
208                 return 0
209         }
210         fmt.Printf(" - created loopback with index: %d\n", reply.SwIfIndex)
211         return reply.SwIfIndex
212 }
213
214 func invokeCreateLoopback(c api.Connection) interface_types.InterfaceIndex {
215         req := &interfaces.CreateLoopback{}
216         reply := &interfaces.CreateLoopbackReply{}
217
218         if err := c.Invoke(context.Background(), req, reply); err != nil {
219                 fmt.Printf("ERROR: %v\n", err)
220                 return 0
221         }
222         fmt.Printf(" - created loopback with index: %d\n", reply.SwIfIndex)
223         return reply.SwIfIndex
224 }
225
226 func deleteLoopback(ch api.Channel, index interface_types.InterfaceIndex) {
227         req := &interfaces.DeleteLoopback{
228                 SwIfIndex: index,
229         }
230         reply := &interfaces.DeleteLoopbackReply{}
231
232         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
233                 fmt.Printf("ERROR: %v\n", err)
234                 return
235         }
236         fmt.Printf(" - deleted loopback with index: %d\n", index)
237 }
238
239 func invokeDeleteLoopback(c api.Connection, index interface_types.InterfaceIndex) {
240         req := &interfaces.DeleteLoopback{
241                 SwIfIndex: index,
242         }
243         reply := &interfaces.DeleteLoopbackReply{}
244
245         if err := c.Invoke(context.Background(), req, reply); err != nil {
246                 fmt.Printf("ERROR: %v\n", err)
247                 return
248         }
249         fmt.Printf(" - deleted loopback with index: %d\n", index)
250 }
251
252 func addIPAddress(addr string, ch api.Channel, index interface_types.InterfaceIndex) {
253         ipAddr, err := ip_types.ParsePrefix(addr)
254         if err != nil {
255                 fmt.Printf("ERROR: %v\n", err)
256                 return
257         }
258
259         req := &interfaces.SwInterfaceAddDelAddress{
260                 SwIfIndex: index,
261                 IsAdd:     true,
262                 Prefix:    ip_types.AddressWithPrefix(ipAddr),
263         }
264         reply := &interfaces.SwInterfaceAddDelAddressReply{}
265
266         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
267                 fmt.Printf("ERROR: %v\n", err)
268                 return
269         }
270         fmt.Printf(" - IP address %s added to interface with index %d\n", addr, index)
271 }
272
273 func invokeAddIPAddress(addr string, c api.Connection, index interface_types.InterfaceIndex) {
274         ipAddr, err := ip_types.ParsePrefix(addr)
275         if err != nil {
276                 fmt.Printf("ERROR: %v\n", err)
277                 return
278         }
279
280         req := &interfaces.SwInterfaceAddDelAddress{
281                 SwIfIndex: index,
282                 IsAdd:     true,
283                 Prefix:    ip_types.AddressWithPrefix(ipAddr),
284         }
285         reply := &interfaces.SwInterfaceAddDelAddressReply{}
286
287         if err := c.Invoke(context.Background(), req, reply); err != nil {
288                 fmt.Printf("ERROR: %v\n", err)
289                 return
290         }
291         fmt.Printf(" - IP address %s added to interface with index %d\n", addr, index)
292 }
293
294 func interfaceDump(ch api.Channel) {
295         reqCtx := ch.SendMultiRequest(&interfaces.SwInterfaceDump{
296                 SwIfIndex: ^interface_types.InterfaceIndex(0),
297         })
298         for {
299                 msg := &interfaces.SwInterfaceDetails{}
300                 stop, err := reqCtx.ReceiveReply(msg)
301                 if stop {
302                         break
303                 }
304                 if err != nil {
305                         fmt.Printf("ERROR: %v\n", err)
306                         return
307                 }
308                 fmt.Printf(" - retrieved interface: %v (idx: %d)\n", msg.InterfaceName, msg.SwIfIndex)
309         }
310 }
311
312 func invokeInterfaceDump(c api.Connection) {
313         s, err := c.NewStream(context.Background())
314         if err != nil {
315                 fmt.Printf("ERROR: %v\n", err)
316                 return
317         }
318         if err := s.SendMsg(&interfaces.SwInterfaceDump{}); err != nil {
319                 fmt.Printf("ERROR: %v\n", err)
320                 return
321         }
322         if err := s.SendMsg(&memclnt.ControlPing{}); err != nil {
323                 fmt.Printf("ERROR: %v\n", err)
324                 return
325         }
326         for {
327                 reply, err := s.RecvMsg()
328                 if err != nil {
329                         fmt.Printf("ERROR: %v\n", err)
330                         return
331                 }
332                 switch msg := reply.(type) {
333                 case *interfaces.SwInterfaceDetails:
334                         fmt.Printf(" - retrieved interface: %v (idx: %d)\n", msg.InterfaceName, msg.SwIfIndex)
335                 case *memclnt.ControlPingReply:
336                         return
337                 }
338         }
339 }
340
341 func printTrace(item *api.Record) {
342         h, m, s := item.Timestamp.Clock()
343         reply := ""
344         if item.IsReceived {
345                 reply = "(reply)"
346         }
347         fmt.Printf("%dh:%dm:%ds:%dns %s %s\n", h, m, s,
348                 item.Timestamp.Nanosecond(), item.Message.GetMessageName(), reply)
349 }