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