1 // Copyright (c) 2020 Cisco and/or its affiliates.
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:
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 // multi-vpp is an example of managing multiple VPPs in single application.
26 "git.fd.io/govpp.git/adapter/socketclient"
27 "git.fd.io/govpp.git/adapter/statsclient"
28 "git.fd.io/govpp.git/api"
29 interfaces "git.fd.io/govpp.git/binapi/interface"
30 "git.fd.io/govpp.git/binapi/interface_types"
31 "git.fd.io/govpp.git/binapi/ip"
32 "git.fd.io/govpp.git/binapi/ip_types"
33 "git.fd.io/govpp.git/binapi/vpe"
34 "git.fd.io/govpp.git/core"
38 binapiSockAddrVpp1 = flag.String("api-sock-1", socketclient.DefaultSocketName, "Path to binary API socket file of the VPP1")
39 statsSockAddrVpp1 = flag.String("stats-sock-1", statsclient.DefaultSocketName, "Path to stats socket file of the VPP1")
40 binapiSockAddrVpp2 = flag.String("api-sock-2", socketclient.DefaultSocketName, "Path to binary API socket file of the VPP2")
41 statsSockAddrVpp2 = flag.String("stats-sock-2", statsclient.DefaultSocketName, "Path to stats socket file of the VPP2")
48 fmt.Println("Starting multi-vpp example")
52 logInfo("Finished with %d errors\n", len(errors))
55 logInfo("Finished successfully\n")
59 // since sockets default to the same value
60 if *binapiSockAddrVpp1 == *binapiSockAddrVpp2 {
61 log.Fatalln("ERROR: identical VPP binapi sockets defined, set at least one of them to a non-default path")
63 if *statsSockAddrVpp1 == *statsSockAddrVpp2 {
64 log.Fatalln("ERROR: identical VPP stats sockets defined, set at least one of them to a non-default path")
66 var name1, name2 = "vpp1", "vpp2"
67 ch1, statsConn1, disconnect1 := connectVPP(name1, *binapiSockAddrVpp1, *statsSockAddrVpp1)
70 ch2, statsConn2, disconnect2 := connectVPP(name2, *binapiSockAddrVpp2, *statsSockAddrVpp2)
75 // retrieve VPP1 version
76 logHeader("Retrieving %s version", name1)
77 getVppVersion(ch1, name1)
79 // retrieve VPP2 version
80 logHeader("Retrieving %s version", name2)
81 getVppVersion(ch1, name2)
84 logHeader("Configuring %s", name1)
85 ifIdx1 := createLoopback(ch1, name1)
86 addIPsToInterface(ch1, ifIdx1, []string{"10.10.0.1/24", "15.10.0.1/24"})
89 logHeader("Configuring %s", name2)
90 ifIdx2 := createLoopback(ch2, name2)
91 addIPsToInterface(ch2, ifIdx2, []string{"20.10.0.1/24", "25.10.0.1/24"})
93 // retrieve configuration from VPPs
94 retrieveIPAddresses(ch1, name1, ifIdx1)
95 retrieveIPAddresses(ch2, name2, ifIdx2)
97 // retrieve stats from VPPs
98 retrieveStats(statsConn1, name1)
99 retrieveStats(statsConn2, name2)
102 logHeader("Cleaning up %s", name1)
103 deleteIPsToInterface(ch1, ifIdx1, []string{"10.10.0.1/24", "15.10.0.1/24"})
104 deleteLoopback(ch1, ifIdx1)
105 logHeader("Cleaning up %s", name2)
106 deleteIPsToInterface(ch2, ifIdx2, []string{"20.10.0.1/24", "25.10.0.1/24"})
107 deleteLoopback(ch2, ifIdx2)
110 func connectVPP(name, binapiSocket, statsSocket string) (api.Channel, api.StatsProvider, func()) {
112 logHeader("Connecting to %s", name)
114 // connect VPP1 to the binapi socket
115 ch, disconnectBinapi, err := connectBinapi(binapiSocket, 1)
117 log.Fatalf("ERROR: connecting VPP binapi failed (socket %s): %v\n", binapiSocket, err)
120 // connect VPP1 to the stats socket
121 statsConn, disconnectStats, err := connectStats(name, statsSocket)
124 log.Fatalf("ERROR: connecting VPP stats failed (socket %s): %v\n", statsSocket, err)
129 return ch, statsConn, func() {
132 logInfo("VPP %s disconnected\n", name)
136 // connectBinapi connects to the binary API socket and returns a communication channel
137 func connectBinapi(socket string, attempts int) (api.Channel, func(), error) {
138 logInfo("Attaching to the binapi socket %s\n", socket)
139 conn, event, err := govpp.AsyncConnect(socket, attempts, core.DefaultReconnectInterval)
145 if e.State != core.Connected {
149 ch, err := getAPIChannel(conn)
153 disconnect := func() {
161 return ch, disconnect, nil
164 // connectStats connects to the stats socket and returns a stats provider
165 func connectStats(name, socket string) (api.StatsProvider, func(), error) {
166 logInfo("Attaching to the stats socket %s\n", socket)
167 sc := statsclient.NewStatsClient(socket)
168 conn, err := core.ConnectStats(sc)
172 disconnect := func() {
173 if err := sc.Disconnect(); err != nil {
174 logError(err, "failed to disconnect "+name+" stats socket")
177 return conn, disconnect, nil
180 // getAPIChannel creates new API channel and verifies its compatibility
181 func getAPIChannel(c api.ChannelProvider) (api.Channel, error) {
182 ch, err := c.NewAPIChannel()
186 if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
189 if err := ch.CheckCompatiblity(interfaces.AllMessages()...); err != nil {
195 // getVppVersion returns VPP version (simple API usage)
196 func getVppVersion(ch api.Channel, name string) {
197 logInfo("Retrieving version of %s ..\n", name)
199 req := &vpe.ShowVersion{}
200 reply := &vpe.ShowVersionReply{}
202 if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
203 logError(err, "retrieving version")
206 logInfo("Retrieved version is %q\n", reply.Version)
210 // createLoopback sends request to create a loopback interface
211 func createLoopback(ch api.Channel, name string) interface_types.InterfaceIndex {
212 logInfo("Adding loopback interface ..\n")
214 req := &interfaces.CreateLoopback{}
215 reply := &interfaces.CreateLoopbackReply{}
217 if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
218 logError(err, "adding loopback interface")
221 logInfo("Interface index %d added to %s\n", reply.SwIfIndex, name)
223 return reply.SwIfIndex
226 // deleteLoopback removes created loopback interface
227 func deleteLoopback(ch api.Channel, ifIdx interface_types.InterfaceIndex) {
228 logInfo("Removing loopback interface ..\n")
229 req := &interfaces.DeleteLoopback{
232 reply := &interfaces.DeleteLoopbackReply{}
234 if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
235 logError(err, "removing loopback interface")
241 // addIPsToInterface sends request to add IP addresses to an interface.
242 func addIPsToInterface(ch api.Channel, index interface_types.InterfaceIndex, ips []string) {
243 for _, ipAddr := range ips {
244 logInfo("Adding IP address %s\n", ipAddr)
245 prefix, err := ip_types.ParsePrefix(ipAddr)
247 logError(err, "attempt to add invalid IP address")
251 req := &interfaces.SwInterfaceAddDelAddress{
254 Prefix: ip_types.AddressWithPrefix(prefix),
256 reply := &interfaces.SwInterfaceAddDelAddressReply{}
258 if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
259 logError(err, "adding IP address to interface")
268 // deleteIPsToInterface sends request to remove IP addresses from an interface.
269 func deleteIPsToInterface(ch api.Channel, index interface_types.InterfaceIndex, ips []string) {
270 for _, ipAddr := range ips {
271 logInfo("Removing IP address %s\n", ipAddr)
272 prefix, err := ip_types.ParsePrefix(ipAddr)
274 logError(err, "attempt to remove invalid IP address")
278 req := &interfaces.SwInterfaceAddDelAddress{
280 Prefix: ip_types.AddressWithPrefix(prefix),
282 reply := &interfaces.SwInterfaceAddDelAddressReply{}
284 if err := ch.SendRequest(req).ReceiveReply(reply); err != nil {
285 logError(err, "removing IP address to interface")
291 // retrieveIPAddresses reads IP address from the interface
292 func retrieveIPAddresses(ch api.Channel, name string, index interface_types.InterfaceIndex) {
293 logHeader("Retrieving interface data from %s", name)
294 req := &ip.IPAddressDump{
297 reqCtx := ch.SendMultiRequest(req)
299 logInfo("Dump IP addresses for interface index %d ..\n", index)
301 msg := &ip.IPAddressDetails{}
302 stop, err := reqCtx.ReceiveReply(msg)
304 logError(err, "dumping IP addresses")
310 prefix := ip_types.Prefix(msg.Prefix)
311 logInfo(" - ip address: %v\n", prefix)
318 // retrieveStats reads interface stats
319 func retrieveStats(s api.StatsProvider, name string) {
320 logHeader("Retrieving interface stats from %s", name)
321 ifStats := &api.InterfaceStats{}
322 err := s.GetInterfaceStats(ifStats)
324 logError(err, "dumping interface stats")
327 logInfo("Dump interface stats ..\n")
328 for _, ifStats := range ifStats.Interfaces {
329 logInfo(" - %+v\n", ifStats)
336 // logHeader prints underlined message (for better output segmentation)
337 func logHeader(format string, a ...interface{}) {
338 n, _ := fmt.Printf(format+"\n", a...)
339 fmt.Println(strings.Repeat("-", n-1))
342 // logInfo prints info message
343 func logInfo(format string, a ...interface{}) {
344 fmt.Printf(format, a...)
347 // logError prints error message
348 func logError(err error, msg string) {
349 fmt.Printf("[ERROR]: %s: %v\n", msg, err)
350 errors = append(errors, err)