From 2d07847237e754d9050f06f565baa430c70ed937 Mon Sep 17 00:00:00 2001 From: Rastislav Szabo Date: Thu, 25 May 2017 13:47:43 +0200 Subject: added async connect API, new structure of examples Change-Id: Iab9bce174596c30998981e02b7030c248c423384 Signed-off-by: Rastislav Szabo --- examples/cmd/simple-client/simple_client.go | 221 ++++++++++++++++++++++ examples/cmd/stats-client/stats_client.go | 132 +++++++++++++ examples/example_client.go | 278 ---------------------------- 3 files changed, 353 insertions(+), 278 deletions(-) create mode 100644 examples/cmd/simple-client/simple_client.go create mode 100644 examples/cmd/stats-client/stats_client.go delete mode 100644 examples/example_client.go (limited to 'examples') diff --git a/examples/cmd/simple-client/simple_client.go b/examples/cmd/simple-client/simple_client.go new file mode 100644 index 0000000..6e46d6b --- /dev/null +++ b/examples/cmd/simple-client/simple_client.go @@ -0,0 +1,221 @@ +// Copyright (c) 2017 Cisco and/or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Binary simple-client is an example VPP management application that exercises the +// govpp API on real-world use-cases. +package main + +// Generates Go bindings for all VPP APIs located in the json directory. +//go:generate binapi-generator --input-dir=bin_api --output-dir=bin_api + +import ( + "fmt" + "net" + "os" + + "git.fd.io/govpp.git" + "git.fd.io/govpp.git/api" + "git.fd.io/govpp.git/examples/bin_api/acl" + "git.fd.io/govpp.git/examples/bin_api/interfaces" + "git.fd.io/govpp.git/examples/bin_api/tap" +) + +func main() { + fmt.Println("Starting simple VPP client...") + + // connect to VPP + conn, err := govpp.Connect() + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + defer conn.Disconnect() + + // create an API channel that will be used in the examples + ch, err := conn.NewAPIChannel() + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + defer ch.Close() + + // check whether the VPP supports our version of some messages + compatibilityCheck(ch) + + // individual examples + aclVersion(ch) + aclConfig(ch) + aclDump(ch) + + tapConnect(ch) + + interfaceDump(ch) + interfaceNotifications(ch) +} + +// compatibilityCheck shows how an management application can check whether generated API messages are +// compatible with the version of VPP which the library is connected to. +func compatibilityCheck(ch *api.Channel) { + err := ch.CheckMessageCompatibility( + &interfaces.SwInterfaceDump{}, + &interfaces.SwInterfaceDetails{}, + ) + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +// aclVersion is the simplest API example - one empty request message and one reply message. +func aclVersion(ch *api.Channel) { + req := &acl.ACLPluginGetVersion{} + reply := &acl.ACLPluginGetVersionReply{} + + err := ch.SendRequest(req).ReceiveReply(reply) + + if err != nil { + fmt.Println("Error:", err) + } else { + fmt.Printf("%+v\n", reply) + } +} + +// aclConfig is another simple API example - in this case, the request contains structured data. +func aclConfig(ch *api.Channel) { + req := &acl.ACLAddReplace{ + ACLIndex: ^uint32(0), + Tag: []byte("access list 1"), + R: []acl.ACLRule{ + { + IsPermit: 1, + SrcIPAddr: net.ParseIP("10.0.0.0").To4(), + SrcIPPrefixLen: 8, + DstIPAddr: net.ParseIP("192.168.1.0").To4(), + DstIPPrefixLen: 24, + Proto: 6, + }, + { + IsPermit: 1, + SrcIPAddr: net.ParseIP("8.8.8.8").To4(), + SrcIPPrefixLen: 32, + DstIPAddr: net.ParseIP("172.16.0.0").To4(), + DstIPPrefixLen: 16, + Proto: 6, + }, + }, + } + reply := &acl.ACLAddReplaceReply{} + + err := ch.SendRequest(req).ReceiveReply(reply) + + if err != nil { + fmt.Println("Error:", err) + } else { + fmt.Printf("%+v\n", reply) + } +} + +// aclDump shows an example where SendRequest and ReceiveReply are not chained together. +func aclDump(ch *api.Channel) { + req := &acl.ACLDump{} + reply := &acl.ACLDetails{} + + reqCtx := ch.SendRequest(req) + err := reqCtx.ReceiveReply(reply) + + if err != nil { + fmt.Println("Error:", err) + } else { + fmt.Printf("%+v\n", reply) + } +} + +// tapConnect example shows how the Go channels in the API channel can be accessed directly instead +// of using SendRequest and ReceiveReply wrappers. +func tapConnect(ch *api.Channel) { + req := &tap.TapConnect{ + TapName: []byte("testtap"), + UseRandomMac: 1, + } + + // send the request to the request go channel + ch.ReqChan <- &api.VppRequest{Message: req} + + // receive a reply from the reply go channel + vppReply := <-ch.ReplyChan + if vppReply.Error != nil { + fmt.Println("Error:", vppReply.Error) + return + } + + // decode the message + reply := &tap.TapConnectReply{} + err := ch.MsgDecoder.DecodeMsg(vppReply.Data, reply) + + if err != nil { + fmt.Println("Error:", err) + } else { + fmt.Printf("%+v\n", reply) + } +} + +// interfaceDump shows an example of multipart request (multiple replies are expected). +func interfaceDump(ch *api.Channel) { + req := &interfaces.SwInterfaceDump{} + reqCtx := ch.SendMultiRequest(req) + + for { + msg := &interfaces.SwInterfaceDetails{} + stop, err := reqCtx.ReceiveReply(msg) + if stop { + break // break out of the loop + } + if err != nil { + fmt.Println("Error:", err) + } + fmt.Printf("%+v\n", msg) + } +} + +// interfaceNotifications shows the usage of notification API. Note that for notifications, +// you are supposed to create your own Go channel with your preferred buffer size. If the channel's +// buffer is full, the notifications will not be delivered into it. +func interfaceNotifications(ch *api.Channel) { + // subscribe for specific notification message + notifChan := make(chan api.Message, 100) + subs, _ := ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceSetFlags) + + // enable interface events in VPP + ch.SendRequest(&interfaces.WantInterfaceEvents{ + Pid: uint32(os.Getpid()), + EnableDisable: 1, + }).ReceiveReply(&interfaces.WantInterfaceEventsReply{}) + + // generate some events in VPP + ch.SendRequest(&interfaces.SwInterfaceSetFlags{ + SwIfIndex: 0, + AdminUpDown: 0, + }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{}) + ch.SendRequest(&interfaces.SwInterfaceSetFlags{ + SwIfIndex: 0, + AdminUpDown: 1, + }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{}) + + // receive one notification + notif := (<-notifChan).(*interfaces.SwInterfaceSetFlags) + fmt.Printf("%+v\n", notif) + + // unsubscribe from delivery of the notifications + ch.UnsubscribeNotification(subs) +} diff --git a/examples/cmd/stats-client/stats_client.go b/examples/cmd/stats-client/stats_client.go new file mode 100644 index 0000000..fc40b24 --- /dev/null +++ b/examples/cmd/stats-client/stats_client.go @@ -0,0 +1,132 @@ +// Copyright (c) 2017 Cisco and/or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Binary stats-client is an example VPP management application that exercises the +// govpp API for interface counters together with asynchronous connection to VPP. +package main + +// Generates Go bindings for all VPP APIs located in the json directory. +//go:generate binapi-generator --input-dir=bin_api --output-dir=bin_api + +import ( + "fmt" + "os" + "os/signal" + + "git.fd.io/govpp.git" + "git.fd.io/govpp.git/api" + "git.fd.io/govpp.git/api/ifcounters" + "git.fd.io/govpp.git/core" + "git.fd.io/govpp.git/core/bin_api/vpe" + "git.fd.io/govpp.git/examples/bin_api/interfaces" +) + +func main() { + fmt.Println("Starting stats VPP client...") + + // async connect to VPP + conn, statCh, err := govpp.AsyncConnect() + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + defer conn.Disconnect() + + // create an API channel that will be used in the examples + ch, err := conn.NewAPIChannel() + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + defer ch.Close() + + // create channel for Interrupt signal + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt) + + var subs *api.NotifSubscription + var notifChan chan api.Message + + // loop until Interrupt signal is received +loop: + for { + select { + + case connEvent := <-statCh: + // VPP connection state change + switch connEvent.State { + case core.Connected: + fmt.Println("VPP connected.") + if subs == nil { + subs, notifChan = subscribeNotification(ch) + } + requestStatistics(ch) + + case core.Disconnected: + fmt.Println("VPP disconnected.") + } + + case notifMsg := <-notifChan: + // counter notification received + processCounters(notifMsg.(*interfaces.VnetInterfaceCounters)) + + case <-sigChan: + // interrupt received + fmt.Println("Interrupt received, exiting.") + break loop + } + } + + ch.UnsubscribeNotification(subs) +} + +// subscribeNotification subscribes for interface counters notifications. +func subscribeNotification(ch *api.Channel) (*api.NotifSubscription, chan api.Message) { + + notifChan := make(chan api.Message, 100) + subs, _ := ch.SubscribeNotification(notifChan, interfaces.NewVnetInterfaceCounters) + + return subs, notifChan +} + +// requestStatistics requests interface counters notifications from VPP. +func requestStatistics(ch *api.Channel) { + ch.SendRequest(&vpe.WantStats{ + Pid: uint32(os.Getpid()), + EnableDisable: 1, + }).ReceiveReply(&vpe.WantStatsReply{}) +} + +// processCounters processes a counter message received from VPP. +func processCounters(msg *interfaces.VnetInterfaceCounters) { + fmt.Printf("%+v\n", msg) + + if msg.IsCombined == 0 { + // simple counter + counters, err := ifcounters.DecodeCounters(ifcounters.VnetInterfaceCounters(*msg)) + if err != nil { + fmt.Println("Error:", err) + } else { + fmt.Printf("%+v\n", counters) + } + } else { + // combined counter + counters, err := ifcounters.DecodeCombinedCounters(ifcounters.VnetInterfaceCounters(*msg)) + if err != nil { + fmt.Println("Error:", err) + } else { + fmt.Printf("%+v\n", counters) + } + } +} diff --git a/examples/example_client.go b/examples/example_client.go deleted file mode 100644 index f2e5804..0000000 --- a/examples/example_client.go +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright (c) 2017 Cisco and/or its affiliates. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Example VPP management application that exercises the govpp API on real-world use-cases. -package main - -// Generates Go bindings for all VPP APIs located in the json directory. -//go:generate binapi-generator --input-dir=bin_api --output-dir=bin_api - -import ( - "fmt" - "net" - "os" - "os/signal" - - "git.fd.io/govpp.git" - "git.fd.io/govpp.git/api" - "git.fd.io/govpp.git/api/ifcounters" - "git.fd.io/govpp.git/core/bin_api/vpe" - "git.fd.io/govpp.git/examples/bin_api/acl" - "git.fd.io/govpp.git/examples/bin_api/interfaces" - "git.fd.io/govpp.git/examples/bin_api/tap" -) - -func main() { - fmt.Println("Starting example VPP client...") - - // connect to VPP - conn, err := govpp.Connect() - if err != nil { - fmt.Println("Error:", err) - os.Exit(1) - } - defer conn.Disconnect() - - // create an API channel that will be used in the examples - ch, err := conn.NewAPIChannel() - if err != nil { - fmt.Println("Error:", err) - os.Exit(1) - } - defer ch.Close() - - // check whether the VPP supports our version of some messages - compatibilityCheck(ch) - - // individual examples - aclVersion(ch) - aclConfig(ch) - aclDump(ch) - - tapConnect(ch) - - interfaceDump(ch) - interfaceNotifications(ch) - - //interfaceCounters(ch) -} - -// compatibilityCheck shows how an management application can check whether generated API messages are -// compatible with the version of VPP which the library is connected to. -func compatibilityCheck(ch *api.Channel) { - err := ch.CheckMessageCompatibility( - &interfaces.SwInterfaceDump{}, - &interfaces.SwInterfaceDetails{}, - ) - if err != nil { - fmt.Println(err) - os.Exit(1) - } -} - -// aclVersion is the simplest API example - one empty request message and one reply message. -func aclVersion(ch *api.Channel) { - req := &acl.ACLPluginGetVersion{} - reply := &acl.ACLPluginGetVersionReply{} - - err := ch.SendRequest(req).ReceiveReply(reply) - - if err != nil { - fmt.Println("Error:", err) - } else { - fmt.Printf("%+v\n", reply) - } -} - -// aclConfig is another simple API example - in this case, the request contains structured data. -func aclConfig(ch *api.Channel) { - req := &acl.ACLAddReplace{ - ACLIndex: ^uint32(0), - Tag: []byte("access list 1"), - R: []acl.ACLRule{ - { - IsPermit: 1, - SrcIPAddr: net.ParseIP("10.0.0.0").To4(), - SrcIPPrefixLen: 8, - DstIPAddr: net.ParseIP("192.168.1.0").To4(), - DstIPPrefixLen: 24, - Proto: 6, - }, - { - IsPermit: 1, - SrcIPAddr: net.ParseIP("8.8.8.8").To4(), - SrcIPPrefixLen: 32, - DstIPAddr: net.ParseIP("172.16.0.0").To4(), - DstIPPrefixLen: 16, - Proto: 6, - }, - }, - } - reply := &acl.ACLAddReplaceReply{} - - err := ch.SendRequest(req).ReceiveReply(reply) - - if err != nil { - fmt.Println("Error:", err) - } else { - fmt.Printf("%+v\n", reply) - } -} - -// aclDump shows an example where SendRequest and ReceiveReply are not chained together. -func aclDump(ch *api.Channel) { - req := &acl.ACLDump{} - reply := &acl.ACLDetails{} - - reqCtx := ch.SendRequest(req) - err := reqCtx.ReceiveReply(reply) - - if err != nil { - fmt.Println("Error:", err) - } else { - fmt.Printf("%+v\n", reply) - } -} - -// tapConnect example shows how the Go channels in the API channel can be accessed directly instead -// of using SendRequest and ReceiveReply wrappers. -func tapConnect(ch *api.Channel) { - req := &tap.TapConnect{ - TapName: []byte("testtap"), - UseRandomMac: 1, - } - - // send the request to the request go channel - ch.ReqChan <- &api.VppRequest{Message: req} - - // receive a reply from the reply go channel - vppReply := <-ch.ReplyChan - if vppReply.Error != nil { - fmt.Println("Error:", vppReply.Error) - return - } - - // decode the message - reply := &tap.TapConnectReply{} - err := ch.MsgDecoder.DecodeMsg(vppReply.Data, reply) - - if err != nil { - fmt.Println("Error:", err) - } else { - fmt.Printf("%+v\n", reply) - } -} - -// interfaceDump shows an example of multipart request (multiple replies are expected). -func interfaceDump(ch *api.Channel) { - req := &interfaces.SwInterfaceDump{} - reqCtx := ch.SendMultiRequest(req) - - for { - msg := &interfaces.SwInterfaceDetails{} - stop, err := reqCtx.ReceiveReply(msg) - if stop { - break // break out of the loop - } - if err != nil { - fmt.Println("Error:", err) - } - fmt.Printf("%+v\n", msg) - } -} - -// interfaceNotifications shows the usage of notification API. Note that for notifications, -// you are supposed to create your own Go channel with your preferred buffer size. If the channel's -// buffer is full, the notifications will not be delivered into it. -func interfaceNotifications(ch *api.Channel) { - // subscribe for specific notification message - notifChan := make(chan api.Message, 100) - subs, _ := ch.SubscribeNotification(notifChan, interfaces.NewSwInterfaceSetFlags) - - // enable interface events in VPP - ch.SendRequest(&interfaces.WantInterfaceEvents{ - Pid: uint32(os.Getpid()), - EnableDisable: 1, - }).ReceiveReply(&interfaces.WantInterfaceEventsReply{}) - - // generate some events in VPP - ch.SendRequest(&interfaces.SwInterfaceSetFlags{ - SwIfIndex: 0, - AdminUpDown: 0, - }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{}) - ch.SendRequest(&interfaces.SwInterfaceSetFlags{ - SwIfIndex: 0, - AdminUpDown: 1, - }).ReceiveReply(&interfaces.SwInterfaceSetFlagsReply{}) - - // receive one notification - notif := (<-notifChan).(*interfaces.SwInterfaceSetFlags) - fmt.Printf("%+v\n", notif) - - // unsubscribe from delivery of the notifications - ch.UnsubscribeNotification(subs) -} - -// interfaceCounters is an example of using notification API to periodically retrieve interface statistics. -// The ifcounters package contains the API that can be used to decode the strange VnetInterfaceCounters message. -func interfaceCounters(ch *api.Channel) { - // subscribe for interface counters notifications - notifChan := make(chan api.Message, 100) - subs, _ := ch.SubscribeNotification(notifChan, interfaces.NewVnetInterfaceCounters) - - // enable interface counters notifications from VPP - ch.SendRequest(&vpe.WantStats{ - Pid: uint32(os.Getpid()), - EnableDisable: 1, - }).ReceiveReply(&vpe.WantStatsReply{}) - - // create channel for Interrupt signal - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, os.Interrupt) - - // loop until Interrupt signal is received -loop: - for { - select { - case <-sigChan: - // interrupt received - break loop - case notifMsg := <-notifChan: - notif := notifMsg.(*interfaces.VnetInterfaceCounters) - // notification received - fmt.Printf("%+v\n", notif) - - if notif.IsCombined == 0 { - // simple counter - counters, err := ifcounters.DecodeCounters(ifcounters.VnetInterfaceCounters(*notif)) - if err != nil { - fmt.Println("Error:", err) - } else { - fmt.Printf("%+v\n", counters) - } - } else { - // combined counter - counters, err := ifcounters.DecodeCombinedCounters(ifcounters.VnetInterfaceCounters(*notif)) - if err != nil { - fmt.Println("Error:", err) - } else { - fmt.Printf("%+v\n", counters) - } - } - } - } - - // unsubscribe from delivery of the notifications - ch.UnsubscribeNotification(subs) -} -- cgit 1.2.3-korg