8714c9a17f54975c3f466f077cc7763b267de522
[govpp.git] / examples / multi-vpp / multi_vpp.go
1 // Copyright (c) 2020 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 // multi-vpp is an example of managing multiple VPPs in single application.
16 package main
17
18 import (
19         "flag"
20         "fmt"
21         "log"
22         "os"
23
24         "git.fd.io/govpp.git"
25         "git.fd.io/govpp.git/adapter/socketclient"
26         "git.fd.io/govpp.git/api"
27         interfaces "git.fd.io/govpp.git/binapi/interface"
28         "git.fd.io/govpp.git/binapi/interface_types"
29         "git.fd.io/govpp.git/binapi/ip"
30         "git.fd.io/govpp.git/binapi/ip_types"
31         "git.fd.io/govpp.git/binapi/vpe"
32         "git.fd.io/govpp.git/core"
33 )
34
35 var (
36         sockAddrVpp1 = flag.String("sock1", socketclient.DefaultSocketName, "Path to binary API socket file of the first VPP instance")
37         sockAddrVpp2 = flag.String("sock2", socketclient.DefaultSocketName, "Path to binary API socket file of the second VPP instance")
38 )
39
40 func main() {
41         flag.Parse()
42         fmt.Println("Starting multi-vpp example")
43
44         // since both of them default to the same value
45         if *sockAddrVpp1 == *sockAddrVpp2 {
46                 log.Fatalln("ERROR: identical VPP sockets defined, set at least one of them to non-default path")
47         }
48
49         // connect VPP1
50         conn1, err := connectToVPP(*sockAddrVpp1, 1)
51         if err != nil {
52                 log.Fatalf("ERROR: connecting VPP failed (socket %s): %v\n", *sockAddrVpp1, err)
53         }
54         defer conn1.Disconnect()
55         ch1, err := getAPIChannel(conn1)
56         if err != nil {
57                 log.Fatalf("ERROR: creating channel failed (socket: %s): %v\n", *sockAddrVpp1, err)
58         }
59         defer ch1.Close()
60
61         // connect VPP2
62         conn2, err := connectToVPP(*sockAddrVpp2, 2)
63         if err != nil {
64                 log.Fatalf("ERROR: connecting VPP failed (socket %s): %v\n", *sockAddrVpp2, err)
65         }
66         defer conn2.Disconnect()
67         ch2, err := getAPIChannel(conn2)
68         if err != nil {
69                 log.Fatalf("ERROR: creating channel failed (socket: %s): %v\n", *sockAddrVpp2, err)
70         }
71         defer ch2.Close()
72
73         // configure VPPs
74         ifIdx1 := createLoopback(ch1)
75         addIPToInterface(ch1, ifIdx1, "10.10.0.1/24")
76         ifIdx2 := createLoopback(ch2)
77         addIPToInterface(ch2, ifIdx2, "20.10.0.1/24")
78
79         // retrieve configuration from the VPPs
80         retrieveIPAddresses(ch1, ifIdx1)
81         retrieveIPAddresses(ch2, ifIdx2)
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 func connectToVPP(socket string, attempts int) (*core.Connection, error) {
92         connection, event, err := govpp.AsyncConnect(socket, attempts, core.DefaultReconnectInterval)
93         if err != nil {
94                 return nil, err
95         }
96
97         // handle connection event
98         select {
99         case e := <-event:
100                 if e.State != core.Connected {
101                         return nil, err
102                 }
103         }
104         return connection, nil
105 }
106
107 func getAPIChannel(conn *core.Connection) (api.Channel, error) {
108         ch, err := conn.NewAPIChannel()
109         if err != nil {
110                 return nil, err
111         }
112
113         if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
114                 return nil, err
115         }
116
117         getVppVersion(ch)
118
119         if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
120                 return nil, err
121         }
122         return ch, nil
123 }
124
125 // getVppVersion returns VPP version (simple API usage)
126 func getVppVersion(ch api.Channel) {
127         fmt.Println("Retrieving version")
128
129         req := &vpe.ShowVersion{}
130         reply := &vpe.ShowVersionReply{}
131
132         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
133                 logError(err, "retrieving version")
134                 return
135         }
136         fmt.Printf("reply: %+v\n", reply)
137
138         fmt.Printf("VPP version: %q\n", reply.Version)
139         fmt.Println("OK")
140         fmt.Println()
141 }
142
143 var Errors []error
144
145 func logError(err error, msg string) {
146         fmt.Printf("ERROR: %s: %v\n", msg, err)
147         Errors = append(Errors, err)
148 }
149
150 // createLoopback sends request to create a loopback interface
151 func createLoopback(ch api.Channel) interface_types.InterfaceIndex {
152         fmt.Println("Adding loopback interface")
153
154         req := &interfaces.CreateLoopback{}
155         reply := &interfaces.CreateLoopbackReply{}
156
157         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
158                 logError(err, "adding loopback interface")
159                 return 0
160         }
161         fmt.Printf("reply: %+v\n", reply)
162
163         fmt.Printf("interface index: %v\n", reply.SwIfIndex)
164         fmt.Println("OK")
165         fmt.Println()
166
167         return reply.SwIfIndex
168 }
169
170 // addIPToInterface sends request to add an IP address to an interface.
171 func addIPToInterface(ch api.Channel, index interface_types.InterfaceIndex, ip string) {
172         fmt.Printf("Setting up IP address to the interface with index %d\n", index)
173         prefix, err := ip_types.ParsePrefix(ip)
174         if err != nil {
175                 logError(err, "attempt to add invalid IP address")
176                 return
177         }
178
179         req := &interfaces.SwInterfaceAddDelAddress{
180                 SwIfIndex: index,
181                 IsAdd:     true,
182                 Prefix:    ip_types.AddressWithPrefix(prefix),
183         }
184         reply := &interfaces.SwInterfaceAddDelAddressReply{}
185
186         if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
187                 logError(err, "adding IP address to interface")
188                 return
189         }
190         fmt.Printf("reply: %+v\n", reply)
191
192         fmt.Println("OK")
193         fmt.Println()
194 }
195
196 func retrieveIPAddresses(ch api.Channel, index interface_types.InterfaceIndex) {
197         fmt.Printf("Retrieving IP addresses for interface index %d\n", index)
198
199         req := &ip.IPAddressDump{
200                 SwIfIndex: index,
201         }
202         reqCtx := ch.SendMultiRequest(req)
203
204         for {
205                 msg := &ip.IPAddressDetails{}
206                 stop, err := reqCtx.ReceiveReply(msg)
207                 if err != nil {
208                         logError(err, "dumping IP addresses")
209                         return
210                 }
211                 if stop {
212                         break
213                 }
214                 prefix := ip_types.Prefix(msg.Prefix)
215                 fmt.Printf(" - ip address: %v\n", prefix)
216         }
217
218         fmt.Println("OK")
219         fmt.Println()
220 }