From 0f46871b4cc45f2c3bd5bdb0aa0f7615795a2c6d Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Fri, 23 Oct 2020 11:40:18 +0200 Subject: [PATCH 01/16] Fix encoding for float64 and generate conversion for Timestamp - fixes encoding/decoding of float64 - uses little endian (contrary to all other types) - generates helper methods for vpe_types.Timestamp type - adds usage code to simple-client and binapi-types examples Change-Id: I2e83eee0629eb67964049406c50c7ee0a692ccaf Signed-off-by: Ondrej Fabry --- binapi/vpe_types/vpe_types.ba.go | 28 ++++++++++++++++++++ binapigen/gen_helpers.go | 47 ++++++++++++++++++++++++++++----- binapigen/gen_helpers_test.go | 27 +++++++++++++++++++ binapigen/generate.go | 2 ++ codec/codec.go | 4 +-- examples/binapi-types/binapi_types.go | 43 ++++++++++++++++++------------ examples/simple-client/simple_client.go | 17 ++++++++++++ 7 files changed, 142 insertions(+), 26 deletions(-) diff --git a/binapi/vpe_types/vpe_types.ba.go b/binapi/vpe_types/vpe_types.ba.go index 473880d..b4d324f 100644 --- a/binapi/vpe_types/vpe_types.ba.go +++ b/binapi/vpe_types/vpe_types.ba.go @@ -15,6 +15,7 @@ package vpe_types import ( "strconv" + "time" api "git.fd.io/govpp.git/api" ) @@ -79,6 +80,33 @@ type Timedelta float64 // Timestamp defines alias 'timestamp'. type Timestamp float64 +func NewTimestamp(t time.Time) Timestamp { + sec := int64(t.Unix()) + nsec := int32(t.Nanosecond()) + ns := float64(sec) + float64(nsec/1e9) + return Timestamp(ns) +} +func (x Timestamp) ToTime() time.Time { + ns := int64(x * 1e9) + sec := ns / 1e9 + nsec := ns % 1e9 + return time.Unix(sec, nsec) +} +func (x Timestamp) String() string { + return x.ToTime().String() +} +func (x *Timestamp) MarshalText() ([]byte, error) { + return []byte(x.ToTime().Format(time.RFC3339Nano)), nil +} +func (x *Timestamp) UnmarshalText(text []byte) error { + t, err := time.Parse(time.RFC3339Nano, string(text)) + if err != nil { + return err + } + *x = NewTimestamp(t) + return nil +} + // Version defines type 'version'. type Version struct { Major uint32 `binapi:"u32,name=major" json:"major,omitempty"` diff --git a/binapigen/gen_helpers.go b/binapigen/gen_helpers.go index 5eafc76..944bfe2 100644 --- a/binapigen/gen_helpers.go +++ b/binapigen/gen_helpers.go @@ -22,6 +22,7 @@ func init() { const ( fmtPkg = GoImportPath("fmt") netPkg = GoImportPath("net") + timePkg = GoImportPath("time") stringsPkg = GoImportPath("strings") ) @@ -185,13 +186,6 @@ func genIPPrefixConversion(g *GenFile, structName string, ipv int) { g.P("func (x ", structName, ") String() string {") g.P(" ip := x.Address.String()") g.P(" return ip + \"/\" + ", strconvPkg.Ident("Itoa"), "(int(x.Len))") - /*if ipv == 4 { - g.P(" mask := ", netPkg.Ident("CIDRMask"), "(int(x.Len), 32)") - } else { - g.P(" mask := ", netPkg.Ident("CIDRMask"), "(int(x.Len), 128)") - } - g.P(" ipnet := &", netPkg.Ident("IPNet"), "{IP: x.Address.ToIP(), Mask: mask}") - g.P(" return ipnet.String()")*/ g.P("}") // MarshalText method @@ -346,3 +340,42 @@ func genMacAddressConversion(g *GenFile, structName string) { g.P("}") g.P() } + +func genTimestampConversion(g *GenFile, structName string) { + // NewTimestamp method + g.P("func New", structName, "(t ", timePkg.Ident("Time"), ") ", structName, " {") + g.P(" sec := int64(t.Unix())") + g.P(" nsec := int32(t.Nanosecond())") + g.P(" ns := float64(sec) + float64(nsec / 1e9)") + g.P(" return ", structName, "(ns)") + g.P("}") + + // ToTime method + g.P("func (x ", structName, ") ToTime() ", timePkg.Ident("Time"), " {") + g.P(" ns := int64(x * 1e9)") + g.P(" sec := ns / 1e9") + g.P(" nsec := ns % 1e9") + g.P(" return ", timePkg.Ident("Unix"), "(sec, nsec)") + g.P("}") + + // String method + g.P("func (x ", structName, ") String() string {") + g.P(" return x.ToTime().String()") + g.P("}") + + // MarshalText method + g.P("func (x *", structName, ") MarshalText() ([]byte, error) {") + g.P(" return []byte(x.ToTime().Format(", timePkg.Ident("RFC3339Nano"), ")), nil") + g.P("}") + + // UnmarshalText method + g.P("func (x *", structName, ") UnmarshalText(text []byte) error {") + g.P(" t, err := ", timePkg.Ident("Parse"), "(", timePkg.Ident("RFC3339Nano"), ", string(text))") + g.P(" if err != nil {") + g.P(" return err") + g.P(" }") + g.P(" *x = New", structName, "(t)") + g.P(" return nil") + g.P("}") + g.P() +} diff --git a/binapigen/gen_helpers_test.go b/binapigen/gen_helpers_test.go index 371fd6c..075dd8e 100644 --- a/binapigen/gen_helpers_test.go +++ b/binapigen/gen_helpers_test.go @@ -17,11 +17,13 @@ package binapigen import ( "strings" "testing" + "time" . "github.com/onsi/gomega" "git.fd.io/govpp.git/binapi/ethernet_types" "git.fd.io/govpp.git/binapi/ip_types" + "git.fd.io/govpp.git/binapi/vpe_types" ) func TestGeneratedParseAddress(t *testing.T) { @@ -154,3 +156,28 @@ func TestGeneratedParseMACError(t *testing.T) { _, err := ethernet_types.ParseMacAddress("malformed_mac") Expect(err).Should(HaveOccurred()) } + +func TestGeneratedParseTimestamp(t *testing.T) { + RegisterTestingT(t) + + var data = []struct { + input time.Time + result vpe_types.Timestamp + }{ + {time.Unix(0, 0), vpe_types.Timestamp(0)}, + {time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), + vpe_types.Timestamp(9.466848e+08)}, + } + + for _, entry := range data { + t.Run(entry.input.String(), func(t *testing.T) { + ts := vpe_types.NewTimestamp(entry.input) + Expect(ts).To(Equal(entry.result)) + + Expect(entry.input.Equal(ts.ToTime())).To(BeTrue()) + + originTime := ts.String() + Expect(originTime).To(Equal(entry.input.Local().String())) + }) + } +} diff --git a/binapigen/generate.go b/binapigen/generate.go index 834c989..bf6df81 100644 --- a/binapigen/generate.go +++ b/binapigen/generate.go @@ -246,6 +246,8 @@ func genAlias(g *GenFile, alias *Alias) { genAddressWithPrefixConversion(g, alias.GoName) case "mac_address": genMacAddressConversion(g, alias.GoName) + case "timestamp": + genTimestampConversion(g, alias.GoName) } } diff --git a/codec/codec.go b/codec/codec.go index 3ae578b..21354a1 100644 --- a/codec/codec.go +++ b/codec/codec.go @@ -156,12 +156,12 @@ func (b *Buffer) DecodeInt64() int64 { } func (b *Buffer) EncodeFloat64(v float64) { - binary.BigEndian.PutUint64(b.buf[b.pos:b.pos+8], math.Float64bits(v)) + binary.LittleEndian.PutUint64(b.buf[b.pos:b.pos+8], math.Float64bits(v)) b.pos += 8 } func (b *Buffer) DecodeFloat64() float64 { - v := math.Float64frombits(binary.BigEndian.Uint64(b.buf[b.pos : b.pos+8])) + v := math.Float64frombits(binary.LittleEndian.Uint64(b.buf[b.pos : b.pos+8])) b.pos += 8 return v } diff --git a/examples/binapi-types/binapi_types.go b/examples/binapi-types/binapi_types.go index 849ad1b..2dbaa3e 100644 --- a/examples/binapi-types/binapi_types.go +++ b/examples/binapi-types/binapi_types.go @@ -18,36 +18,42 @@ package main import ( "fmt" "log" + "time" "git.fd.io/govpp.git/binapi/ethernet_types" "git.fd.io/govpp.git/binapi/ip" "git.fd.io/govpp.git/binapi/ip_types" + "git.fd.io/govpp.git/binapi/vpe_types" "git.fd.io/govpp.git/codec" ) func init() { - log.SetFlags(0) } func main() { - addressUnionExample() - ipAddressExample() + log.SetFlags(0) + + usageUnion() + usageAddress() - // convert IP from string form into Address type containing union - convertIP("10.10.1.1") - convertIP("ff80::1") + // convert IP address in string form into ip_types.Address + convertIPAddress("10.10.1.1") + convertIPAddress("ff80::1") - // convert IP from string form into Prefix type + // convert IP address / CIDR in string form into ip_types.Prefix convertIPPrefix("20.10.1.1/24") convertIPPrefix("21.10.1.1") convertIPPrefix("ff90::1/64") convertIPPrefix("ff91::1") - // convert MAC address from string into MacAddress + // convert MAC address in string form into ethernet_types.MacAddress convertToMacAddress("00:10:ab:4f:00:01") + + // convert time.Time into vpe_types.Timestamp + convertToTimestamp(time.Now()) } -func addressUnionExample() { +func usageUnion() { var union ip_types.AddressUnion // initialize union using constructors @@ -63,21 +69,17 @@ func addressUnionExample() { union.SetIP6(ip6) } -func ipAddressExample() { +func usageAddress() { // parse string into IP address - addrIP4, err := ip_types.ParseAddress("192.168.1.10") + addr, err := ip_types.ParseAddress("192.168.1.10") if err != nil { panic(err) } - /*addrIP6, err := ip_types.ParseAddress("ff:2::2") - if err != nil { - panic(err) - }*/ var msg = ip.IPPuntRedirect{ IsAdd: true, Punt: ip.PuntRedirect{ - Nh: addrIP4, + Nh: addr, }, } @@ -103,7 +105,7 @@ func ipAddressExample() { } } -func convertIP(ip string) { +func convertIPAddress(ip string) { addr, err := ip_types.ParseAddress(ip) if err != nil { log.Printf("error converting IP to Address: %v", err) @@ -135,3 +137,10 @@ func convertToMacAddress(mac string) { fmt.Printf("MacAddress converted back to string %#v to: %s\n", parsedMac, parsedMac) } + +func convertToTimestamp(t time.Time) { + timestamp := vpe_types.NewTimestamp(t) + fmt.Printf("converted time %v to: %#v\n", t, timestamp) + + fmt.Printf("Timestamp converted back to string %#v to: %s\n", timestamp, timestamp) +} diff --git a/examples/simple-client/simple_client.go b/examples/simple-client/simple_client.go index 0898c0a..10a0ea6 100644 --- a/examples/simple-client/simple_client.go +++ b/examples/simple-client/simple_client.go @@ -84,6 +84,7 @@ func main() { // use request/reply (channel API) getVppVersion(ch) + getSystemTime(ch) idx := createLoopback(ch) interfaceDump(ch) addIPAddress(ch, idx) @@ -107,6 +108,22 @@ func getVppVersion(ch api.Channel) { fmt.Println() } +func getSystemTime(ch api.Channel) { + fmt.Println("Retrieving system time..") + + req := &vpe.ShowVpeSystemTime{} + reply := &vpe.ShowVpeSystemTimeReply{} + + if err := ch.SendRequest(req).ReceiveReply(reply); err != nil { + logError(err, "retrieving system time") + return + } + + fmt.Printf("system time: %v\n", reply.VpeSystemTime) + fmt.Println("OK") + fmt.Println() +} + func createLoopback(ch api.Channel) interface_types.InterfaceIndex { fmt.Println("Creating loopback interface..") -- 2.16.6 From b74e3b37479a7fa763bed1f2c76de612ee51dcbc Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Thu, 29 Oct 2020 12:56:24 +0100 Subject: [PATCH 02/16] Stats API: added GetMemory() Retrieved numbers relate to the statseg heap. Change-Id: I72750183db3524481918c71c993b39e65c28ddb6 Signed-off-by: Vladimir Lavor --- api/stats.go | 7 +++++++ core/stats.go | 27 +++++++++++++++++++++++++++ examples/multi-vpp/multi_vpp.go | 8 ++++---- examples/simple-client/simple_client.go | 8 ++++---- examples/stats-client/stats_api.go | 9 ++++++++- examples/stream-client/stream_client.go | 8 ++++---- proxy/client.go | 10 ++++++++++ proxy/server.go | 4 ++++ 8 files changed, 68 insertions(+), 13 deletions(-) diff --git a/api/stats.go b/api/stats.go index 2850b5f..9e1ba75 100644 --- a/api/stats.go +++ b/api/stats.go @@ -21,6 +21,7 @@ type StatsProvider interface { GetInterfaceStats(*InterfaceStats) error GetErrorStats(*ErrorStats) error GetBufferStats(*BufferStats) error + GetMemoryStats(*MemoryStats) error } // SystemStats represents global system statistics. @@ -113,3 +114,9 @@ type BufferPool struct { Used float64 Available float64 } + +// MemoryStats represents memory stats segment counters. +type MemoryStats struct { + Total float64 + Used float64 +} diff --git a/core/stats.go b/core/stats.go index cd93cc1..f2da494 100644 --- a/core/stats.go +++ b/core/stats.go @@ -39,6 +39,10 @@ const ( CounterStatsPrefix = "/err/" + MemoryStatPrefix = "/mem/statseg" + MemoryStats_Total = "total" + MemoryStats_Used = "used" + InterfaceStatsPrefix = "/if/" InterfaceStats_Names = InterfaceStatsPrefix + "names" InterfaceStats_Drops = InterfaceStatsPrefix + "drops" @@ -80,6 +84,7 @@ type StatsConnection struct { ifaceStatsData *adapter.StatDir sysStatsData *adapter.StatDir bufStatsData *adapter.StatDir + memStatsData *adapter.StatDir } func newStatsConnection(stats adapter.StatsAPI) *StatsConnection { @@ -471,3 +476,25 @@ func (c *StatsConnection) GetBufferStats(bufStats *api.BufferStats) (err error) return nil } + +func (c *StatsConnection) GetMemoryStats(memStats *api.MemoryStats) (err error) { + if err := c.updateStats(&c.memStatsData, MemoryStatPrefix); err != nil { + return err + } + + for _, stat := range c.memStatsData.Entries { + _, f := path.Split(string(stat.Name)) + var val float64 + m, ok := stat.Data.(adapter.ScalarStat) + if ok { + val = float64(m) + } + switch f { + case MemoryStats_Total: + memStats.Total = val + case MemoryStats_Used: + memStats.Used = val + } + } + return nil +} diff --git a/examples/multi-vpp/multi_vpp.go b/examples/multi-vpp/multi_vpp.go index c42f802..b207ae4 100644 --- a/examples/multi-vpp/multi_vpp.go +++ b/examples/multi-vpp/multi_vpp.go @@ -41,15 +41,15 @@ var ( statsSockAddrVpp2 = flag.String("stats-sock-2", statsclient.DefaultSocketName, "Path to stats socket file of the VPP2") ) -var Errors []error +var errors []error func main() { flag.Parse() fmt.Println("Starting multi-vpp example") defer func() { - if len(Errors) > 0 { - logInfo("Finished with %d errors\n", len(Errors)) + if len(errors) > 0 { + logInfo("Finished with %d errors\n", len(errors)) os.Exit(1) } else { logInfo("Finished successfully\n") @@ -347,5 +347,5 @@ func logInfo(format string, a ...interface{}) { // logError prints error message func logError(err error, msg string) { fmt.Printf("[ERROR]: %s: %v\n", msg, err) - Errors = append(Errors, err) + errors = append(errors, err) } diff --git a/examples/simple-client/simple_client.go b/examples/simple-client/simple_client.go index 10a0ea6..3be2447 100644 --- a/examples/simple-client/simple_client.go +++ b/examples/simple-client/simple_client.go @@ -74,8 +74,8 @@ func main() { // process errors encountered during the example defer func() { - if len(Errors) > 0 { - fmt.Printf("finished with %d errors\n", len(Errors)) + if len(errors) > 0 { + fmt.Printf("finished with %d errors\n", len(errors)) os.Exit(1) } else { fmt.Println("finished successfully") @@ -302,9 +302,9 @@ func marshal(v interface{}) { fmt.Printf("JSON: %s\n", b) } -var Errors []error +var errors []error func logError(err error, msg string) { fmt.Printf("ERROR: %s: %v\n", msg, err) - Errors = append(Errors, err) + errors = append(errors, err) } diff --git a/examples/stats-client/stats_api.go b/examples/stats-client/stats_api.go index 288caea..b1846a6 100644 --- a/examples/stats-client/stats_api.go +++ b/examples/stats-client/stats_api.go @@ -43,7 +43,7 @@ var ( func init() { flag.Usage = func() { - fmt.Fprintf(os.Stderr, "%s: usage [ls|dump|poll|errors|interfaces|nodes|system|buffers] ...\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "%s: usage [ls|dump|poll|errors|interfaces|nodes|system|buffers|memory] ...\n", os.Args[0]) flag.PrintDefaults() os.Exit(1) } @@ -129,6 +129,13 @@ func main() { } fmt.Printf("Buffer stats: %+v\n", stats) + case "memory": + stats := new(api.MemoryStats) + if err := c.GetMemoryStats(stats); err != nil { + log.Fatalln("getting memory stats failed:", err) + } + fmt.Printf("Memory stats: %+v\n", stats) + case "dump": fmt.Printf("Dumping stats.. %s\n", strings.Join(patterns, " ")) diff --git a/examples/stream-client/stream_client.go b/examples/stream-client/stream_client.go index fadfe23..f4af859 100644 --- a/examples/stream-client/stream_client.go +++ b/examples/stream-client/stream_client.go @@ -76,8 +76,8 @@ func main() { // process errors encountered during the example defer func() { - if len(Errors) > 0 { - fmt.Printf("finished with %d errors\n", len(Errors)) + if len(errors) > 0 { + fmt.Printf("finished with %d errors\n", len(errors)) os.Exit(1) } else { fmt.Println("finished successfully") @@ -294,9 +294,9 @@ Loop: fmt.Println() } -var Errors []error +var errors []error func logError(err error, msg string) { fmt.Printf("ERROR: %s: %v\n", msg, err) - Errors = append(Errors, err) + errors = append(errors, err) } diff --git a/proxy/client.go b/proxy/client.go index 6f29c71..aea9a94 100644 --- a/proxy/client.go +++ b/proxy/client.go @@ -117,6 +117,16 @@ func (s *StatsClient) GetBufferStats(bufStats *api.BufferStats) error { return nil } +func (s *StatsClient) GetMemoryStats(memStats *api.MemoryStats) error { + req := StatsRequest{StatsType: "memory"} + resp := StatsResponse{MemStats: new(api.MemoryStats)} + if err := s.rpc.Call("StatsRPC.GetStats", req, &resp); err != nil { + return err + } + *memStats = *resp.MemStats + return nil +} + type BinapiClient struct { rpc *rpc.Client timeout time.Duration diff --git a/proxy/server.go b/proxy/server.go index 50a0077..21d8e1b 100644 --- a/proxy/server.go +++ b/proxy/server.go @@ -55,6 +55,7 @@ type StatsResponse struct { IfaceStats *api.InterfaceStats ErrStats *api.ErrorStats BufStats *api.BufferStats + MemStats *api.MemoryStats } // StatsRPC is a RPC server for proxying client request to api.StatsProvider. @@ -200,6 +201,9 @@ func (s *StatsRPC) GetStats(req StatsRequest, resp *StatsResponse) error { case "buffer": resp.BufStats = new(api.BufferStats) return s.statsConn.GetBufferStats(resp.BufStats) + case "memory": + resp.MemStats = new(api.MemoryStats) + return s.statsConn.GetMemoryStats(resp.MemStats) default: return fmt.Errorf("unknown stats type: %s", req.StatsType) } -- 2.16.6 From 2b743eede78b6fed115421716888f80088edefdb Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Wed, 4 Nov 2020 13:51:00 +0100 Subject: [PATCH 03/16] Update changelog Change-Id: I08f5ba1fb5ec02717e5715447837ca3b75fa894d Signed-off-by: Vladimir Lavor --- CHANGELOG.md | 11 +++++++---- examples/binapi-types/binapi_types.go | 3 ++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bd9357..2bffb4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,17 +29,19 @@ This file lists changes for the GoVPP releases. - generated code now contains comment with information about versions of VPP and binapi-generator - in addition to the file name, the binapi generator now accepts full path (including extension, e.g. `/usr/share/vpp/api/core/vpe.api.json`) - dependency on `github.com/lunixbochs/struc` was removed +- generated helper methods for `vpe_types.Timestamp` ### Features - [socketclient](adapter/socketclient) received a new method to add client name - added list of compatible messages to `CompatibilityError` -- removed global binary API adapter - this change allows GoVPP to manage multiple VPP connections with different +- removed global binary API adapter - this change allows GoVPP to manage multiple VPP connections with different sockets simultaneously - added support for the stats v2. The statsclient adapter recognized the version automatically so the `StatsAPI` remained unchanged. In relation to this change, the legacy support (i.e. stat segment v0) for VPP <=19.04 was dropped. - GoVPP now recognizes VPP state `NotResponding` which can be used to prevent disconnecting in case the VPP hangs or is overloaded -- added method `SetLogger` for setting the global logger +- added method `SetLogger()` for setting the global logger +- `StatsAPI` has a new method `GetMemory()` retrieving values related to the statseg memory heap ### Fixes - `MsgCodec` will recover panic occurring during a message decoding @@ -50,6 +52,7 @@ This file lists changes for the GoVPP releases. - fixed name conflict in generated union field constructors - size of unions composed of another unions is now calculated correctly - fixed race condition in the VPP adapter mock +- fixed crash caused by return value of uint kind ### Other - improved log messages to provide more relevant info @@ -58,12 +61,12 @@ This file lists changes for the GoVPP releases. threshold was increased to 2 (up from 1) #### Examples -- added more code samples of working with unions in [union example](examples/union-example) +- added more code samples of working with unions in [binapi-types](examples/binapi-types) - added profiling mode to [perf bench](examples/perf-bench) example - improved [simple client](examples/simple-client) example to work properly even with multiple runs - added [multi-vpp](examples/multi-vpp) example displaying management of two VPP instances from single application -- added [stream-client](examples/stream-client) example showing usage of the new stream API +- added [stream-client](examples/stream-client) example showing usage of the new stream API #### Dependencies - updated `github.com/sirupsen/logrus` dep to `v1.6.0` diff --git a/examples/binapi-types/binapi_types.go b/examples/binapi-types/binapi_types.go index 2dbaa3e..ae650a1 100644 --- a/examples/binapi-types/binapi_types.go +++ b/examples/binapi-types/binapi_types.go @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// union-example is an example to show how to use unions in VPP binary API. +// binapi-types is an example showing how to use and convert data with +// helper methods from *-types packages in VPP binary API. package main import ( -- 2.16.6 From bcf3fbd21aa22d1546bc85ffb887ae5ba557808e Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Tue, 1 Dec 2020 13:57:29 +0100 Subject: [PATCH 04/16] Fixed incorrect message error in the stream API The message package is passed to the stream object and used to evaluate correct reply message type Change-Id: I2c9844d6447d024af1693205efd5721e2f89f22d Signed-off-by: Vladimir Lavor --- adapter/mock/mock_vpp_adapter.go | 28 ++++++++------ api/binapi.go | 22 +++++------ cmd/vpp-proxy/main.go | 6 ++- core/channel.go | 7 +++- core/connection.go | 84 +++++++++++++++++++++------------------- core/request_handler.go | 22 +++++++++-- core/stream.go | 12 +++++- proxy/server.go | 42 ++++++++++++-------- 8 files changed, 137 insertions(+), 86 deletions(-) diff --git a/adapter/mock/mock_vpp_adapter.go b/adapter/mock/mock_vpp_adapter.go index f79bb8b..90195e7 100644 --- a/adapter/mock/mock_vpp_adapter.go +++ b/adapter/mock/mock_vpp_adapter.go @@ -44,7 +44,7 @@ type VppAdapter struct { access sync.RWMutex msgNameToIds map[string]uint16 msgIDsToName map[uint16]string - binAPITypes map[string]reflect.Type + binAPITypes map[string]map[string]reflect.Type repliesLock sync.Mutex // mutex for the queue replies []reply // FIFO queue of messages @@ -126,7 +126,7 @@ func NewVppAdapter() *VppAdapter { msgIDSeq: 1000, msgIDsToName: make(map[uint16]string), msgNameToIds: make(map[string]uint16), - binAPITypes: make(map[string]reflect.Type), + binAPITypes: make(map[string]map[string]reflect.Type), } a.registerBinAPITypes() return a @@ -165,19 +165,25 @@ func (a *VppAdapter) GetMsgNameByID(msgID uint16) (string, bool) { func (a *VppAdapter) registerBinAPITypes() { a.access.Lock() defer a.access.Unlock() - for _, msg := range api.GetRegisteredMessages() { - a.binAPITypes[msg.GetMessageName()] = reflect.TypeOf(msg).Elem() + for pkg, msgs := range api.GetRegisteredMessages() { + msgMap := make(map[string]reflect.Type) + for _, msg := range msgs { + msgMap[msg.GetMessageName()] = reflect.TypeOf(msg).Elem() + } + a.binAPITypes[pkg] = msgMap } } // ReplyTypeFor returns reply message type for given request message name. -func (a *VppAdapter) ReplyTypeFor(requestMsgName string) (reflect.Type, uint16, bool) { +func (a *VppAdapter) ReplyTypeFor(pkg, requestMsgName string) (reflect.Type, uint16, bool) { replyName, foundName := binapi.ReplyNameFor(requestMsgName) if foundName { - if reply, found := a.binAPITypes[replyName]; found { - msgID, err := a.GetMsgID(replyName, "") - if err == nil { - return reply, msgID, found + if messages, found := a.binAPITypes[pkg]; found { + if reply, found := messages[replyName]; found { + msgID, err := a.GetMsgID(replyName, "") + if err == nil { + return reply, msgID, found + } } } } @@ -186,8 +192,8 @@ func (a *VppAdapter) ReplyTypeFor(requestMsgName string) (reflect.Type, uint16, } // ReplyFor returns reply message for given request message name. -func (a *VppAdapter) ReplyFor(requestMsgName string) (api.Message, uint16, bool) { - replType, msgID, foundReplType := a.ReplyTypeFor(requestMsgName) +func (a *VppAdapter) ReplyFor(pkg, requestMsgName string) (api.Message, uint16, bool) { + replType, msgID, foundReplType := a.ReplyTypeFor(pkg, requestMsgName) if foundReplType { msgVal := reflect.New(replType) if msg, ok := msgVal.Interface().(api.Message); ok { diff --git a/api/binapi.go b/api/binapi.go index cb4ab85..1b07a7e 100644 --- a/api/binapi.go +++ b/api/binapi.go @@ -15,7 +15,7 @@ package api import ( - "fmt" + "path" "reflect" ) @@ -59,27 +59,27 @@ type DataType interface { } var ( - registeredMessageTypes = make(map[reflect.Type]string) - registeredMessages = make(map[string]Message) + registeredMessages = make(map[string]map[string]Message) + registeredMessageTypes = make(map[string]map[reflect.Type]string) ) // RegisterMessage is called from generated code to register message. func RegisterMessage(x Message, name string) { - typ := reflect.TypeOf(x) - namecrc := x.GetMessageName() + "_" + x.GetCrcString() - if _, ok := registeredMessageTypes[typ]; ok { - panic(fmt.Errorf("govpp: message type %v already registered as %s (%s)", typ, name, namecrc)) + binapiPath := path.Dir(reflect.TypeOf(x).Elem().PkgPath()) + if _, ok := registeredMessages[binapiPath]; !ok { + registeredMessages[binapiPath] = make(map[string]Message) + registeredMessageTypes[binapiPath] = make(map[reflect.Type]string) } - registeredMessages[namecrc] = x - registeredMessageTypes[typ] = name + registeredMessages[binapiPath][x.GetMessageName()+"_"+x.GetCrcString()] = x + registeredMessageTypes[binapiPath][reflect.TypeOf(x)] = name } // GetRegisteredMessages returns list of all registered messages. -func GetRegisteredMessages() map[string]Message { +func GetRegisteredMessages() map[string]map[string]Message { return registeredMessages } // GetRegisteredMessageTypes returns list of all registered message types. -func GetRegisteredMessageTypes() map[reflect.Type]string { +func GetRegisteredMessageTypes() map[string]map[reflect.Type]string { return registeredMessageTypes } diff --git a/cmd/vpp-proxy/main.go b/cmd/vpp-proxy/main.go index d1af5df..3c85bcf 100644 --- a/cmd/vpp-proxy/main.go +++ b/cmd/vpp-proxy/main.go @@ -35,8 +35,10 @@ var ( ) func init() { - for _, msg := range api.GetRegisteredMessages() { - gob.Register(msg) + for _, msgList := range api.GetRegisteredMessages() { + for _, msg := range msgList { + gob.Register(msg) + } } } diff --git a/core/channel.go b/core/channel.go index 28d0710..fbb3e59 100644 --- a/core/channel.go +++ b/core/channel.go @@ -45,8 +45,10 @@ type MessageCodec interface { type MessageIdentifier interface { // GetMessageID returns message identifier of given API message. GetMessageID(msg api.Message) (uint16, error) + // GetMessagePath returns path for the given message + GetMessagePath(msg api.Message) string // LookupByID looks up message name and crc by ID - LookupByID(msgID uint16) (api.Message, error) + LookupByID(path string, msgID uint16) (api.Message, error) } // vppRequest is a request that will be sent to VPP. @@ -329,7 +331,8 @@ func (ch *Channel) processReply(reply *vppReply, expSeqNum uint16, msg api.Messa if reply.msgID != expMsgID { var msgNameCrc string - if replyMsg, err := ch.msgIdentifier.LookupByID(reply.msgID); err != nil { + pkgPath := ch.msgIdentifier.GetMessagePath(msg) + if replyMsg, err := ch.msgIdentifier.LookupByID(pkgPath, reply.msgID); err != nil { msgNameCrc = err.Error() } else { msgNameCrc = getMsgNameWithCrc(replyMsg) diff --git a/core/connection.go b/core/connection.go index 0f54f38..f3ff964 100644 --- a/core/connection.go +++ b/core/connection.go @@ -17,6 +17,7 @@ package core import ( "errors" "fmt" + "path" "reflect" "sync" "sync/atomic" @@ -103,9 +104,9 @@ type Connection struct { connChan chan ConnectionEvent // connection status events are sent to this channel - codec MessageCodec // message codec - msgIDs map[string]uint16 // map of message IDs indexed by message name + CRC - msgMap map[uint16]api.Message // map of messages indexed by message ID + codec MessageCodec // message codec + msgIDs map[string]uint16 // map of message IDs indexed by message name + CRC + msgMapByPath map[string]map[uint16]api.Message // map of messages indexed by message ID which are indexed by path maxChannelID uint32 // maximum used channel ID (the real limit is 2^15, 32-bit is used for atomic operations) channelsLock sync.RWMutex // lock for the channels map @@ -139,7 +140,7 @@ func newConnection(binapi adapter.VppAPI, attempts int, interval time.Duration) connChan: make(chan ConnectionEvent, NotificationChanBufSize), codec: codec.DefaultCodec, msgIDs: make(map[string]uint16), - msgMap: make(map[uint16]api.Message), + msgMapByPath: make(map[string]map[uint16]api.Message), channels: make(map[uint16]*Channel), subscriptions: make(map[uint16][]*subscriptionCtx), msgControlPing: msgControlPing, @@ -400,69 +401,74 @@ func (c *Connection) GetMessageID(msg api.Message) (uint16, error) { if c == nil { return 0, errors.New("nil connection passed in") } - - if msgID, ok := c.msgIDs[getMsgNameWithCrc(msg)]; ok { - return msgID, nil - } - + pkgPath := c.GetMessagePath(msg) msgID, err := c.vppClient.GetMsgID(msg.GetMessageName(), msg.GetCrcString()) if err != nil { return 0, err } - + if pathMsgs, pathOk := c.msgMapByPath[pkgPath]; !pathOk { + c.msgMapByPath[pkgPath] = make(map[uint16]api.Message) + c.msgMapByPath[pkgPath][msgID] = msg + } else if _, msgOk := pathMsgs[msgID]; !msgOk { + c.msgMapByPath[pkgPath][msgID] = msg + } + if _, ok := c.msgIDs[getMsgNameWithCrc(msg)]; ok { + return msgID, nil + } c.msgIDs[getMsgNameWithCrc(msg)] = msgID - c.msgMap[msgID] = msg - return msgID, nil } // LookupByID looks up message name and crc by ID. -func (c *Connection) LookupByID(msgID uint16) (api.Message, error) { +func (c *Connection) LookupByID(path string, msgID uint16) (api.Message, error) { if c == nil { return nil, errors.New("nil connection passed in") } - - if msg, ok := c.msgMap[msgID]; ok { + if msg, ok := c.msgMapByPath[path][msgID]; ok { return msg, nil } + return nil, fmt.Errorf("unknown message ID %d for path '%s'", msgID, path) +} - return nil, fmt.Errorf("unknown message ID: %d", msgID) +// GetMessagePath returns path for the given message +func (c *Connection) GetMessagePath(msg api.Message) string { + return path.Dir(reflect.TypeOf(msg).Elem().PkgPath()) } // retrieveMessageIDs retrieves IDs for all registered messages and stores them in map func (c *Connection) retrieveMessageIDs() (err error) { t := time.Now() - msgs := api.GetRegisteredMessages() + msgsByPath := api.GetRegisteredMessages() var n int - for name, msg := range msgs { - typ := reflect.TypeOf(msg).Elem() - path := fmt.Sprintf("%s.%s", typ.PkgPath(), typ.Name()) + for pkgPath, msgs := range msgsByPath { + for _, msg := range msgs { + msgID, err := c.GetMessageID(msg) + if err != nil { + if debugMsgIDs { + log.Debugf("retrieving message ID for %s.%s failed: %v", + pkgPath, msg.GetMessageName(), err) + } + continue + } + n++ + + if c.pingReqID == 0 && msg.GetMessageName() == c.msgControlPing.GetMessageName() { + c.pingReqID = msgID + c.msgControlPing = reflect.New(reflect.TypeOf(msg).Elem()).Interface().(api.Message) + } else if c.pingReplyID == 0 && msg.GetMessageName() == c.msgControlPingReply.GetMessageName() { + c.pingReplyID = msgID + c.msgControlPingReply = reflect.New(reflect.TypeOf(msg).Elem()).Interface().(api.Message) + } - msgID, err := c.GetMessageID(msg) - if err != nil { if debugMsgIDs { - log.Debugf("retrieving message ID for %s failed: %v", path, err) + log.Debugf("message %q (%s) has ID: %d", msg.GetMessageName(), getMsgNameWithCrc(msg), msgID) } - continue - } - n++ - - if c.pingReqID == 0 && msg.GetMessageName() == c.msgControlPing.GetMessageName() { - c.pingReqID = msgID - c.msgControlPing = reflect.New(reflect.TypeOf(msg).Elem()).Interface().(api.Message) - } else if c.pingReplyID == 0 && msg.GetMessageName() == c.msgControlPingReply.GetMessageName() { - c.pingReplyID = msgID - c.msgControlPingReply = reflect.New(reflect.TypeOf(msg).Elem()).Interface().(api.Message) - } - - if debugMsgIDs { - log.Debugf("message %q (%s) has ID: %d", name, getMsgNameWithCrc(msg), msgID) } + log.WithField("took", time.Since(t)). + Debugf("retrieved IDs for %d messages (registered %d) from path %s", n, len(msgs), pkgPath) } - log.WithField("took", time.Since(t)). - Debugf("retrieved IDs for %d messages (registered %d)", n, len(msgs)) return nil } diff --git a/core/request_handler.go b/core/request_handler.go index fc704cb..f9d972a 100644 --- a/core/request_handler.go +++ b/core/request_handler.go @@ -210,9 +210,9 @@ func (c *Connection) msgCallback(msgID uint16, data []byte) { return } - msg, ok := c.msgMap[msgID] - if !ok { - log.Warnf("Unknown message received, ID: %d", msgID) + msg, err := c.getMessageByID(msgID) + if err != nil { + log.Warnln(err) return } @@ -419,3 +419,19 @@ func compareSeqNumbers(seqNum1, seqNum2 uint16) int { } return 1 } + +// Returns first message from any package where the message ID matches +// Note: the msg is further used only for its MessageType which is not +// affected by the message's package +func (c *Connection) getMessageByID(msgID uint16) (msg api.Message, err error) { + var ok bool + for _, msgs := range c.msgMapByPath { + if msg, ok = msgs[msgID]; ok { + break + } + } + if !ok { + return nil, fmt.Errorf("unknown message received, ID: %d", msgID) + } + return msg, nil +} diff --git a/core/stream.go b/core/stream.go index abe9d55..3d417f1 100644 --- a/core/stream.go +++ b/core/stream.go @@ -19,6 +19,7 @@ import ( "errors" "fmt" "reflect" + "sync" "sync/atomic" "time" @@ -34,6 +35,9 @@ type Stream struct { requestSize int replySize int replyTimeout time.Duration + // per-request context + pkgPath string + sync.Mutex } func (c *Connection) NewStream(ctx context.Context, options ...api.StreamOption) (api.Stream, error) { @@ -109,6 +113,9 @@ func (s *Stream) SendMsg(msg api.Message) error { if err := s.conn.processRequest(s.channel, req); err != nil { return err } + s.Lock() + s.pkgPath = s.conn.GetMessagePath(msg) + s.Unlock() return nil } @@ -118,7 +125,10 @@ func (s *Stream) RecvMsg() (api.Message, error) { return nil, err } // resolve message type - msg, err := s.channel.msgIdentifier.LookupByID(reply.msgID) + s.Lock() + path := s.pkgPath + s.Unlock() + msg, err := s.channel.msgIdentifier.LookupByID(path, reply.msgID) if err != nil { return nil, err } diff --git a/proxy/server.go b/proxy/server.go index 21d8e1b..e395468 100644 --- a/proxy/server.go +++ b/proxy/server.go @@ -226,8 +226,8 @@ type BinapiCompatibilityRequest struct { } type BinapiCompatibilityResponse struct { - CompatibleMsgs []string - IncompatibleMsgs []string + CompatibleMsgs map[string][]string + IncompatibleMsgs map[string][]string } // BinapiRPC is a RPC server for proxying client request to api.Channel. @@ -379,25 +379,33 @@ func (s *BinapiRPC) Compatibility(req BinapiCompatibilityRequest, resp *BinapiCo } defer ch.Close() - resp.CompatibleMsgs = make([]string, 0, len(req.MsgNameCrcs)) - resp.IncompatibleMsgs = make([]string, 0, len(req.MsgNameCrcs)) + resp.CompatibleMsgs = make(map[string][]string) + resp.IncompatibleMsgs = make(map[string][]string) - for _, msg := range req.MsgNameCrcs { - val, ok := api.GetRegisteredMessages()[msg] - if !ok { - resp.IncompatibleMsgs = append(resp.IncompatibleMsgs, msg) - continue + for path, messages := range api.GetRegisteredMessages() { + if resp.IncompatibleMsgs[path] == nil { + resp.IncompatibleMsgs[path] = make([]string, 0, len(req.MsgNameCrcs)) } - - if err = ch.CheckCompatiblity(val); err != nil { - resp.IncompatibleMsgs = append(resp.IncompatibleMsgs, msg) - } else { - resp.CompatibleMsgs = append(resp.CompatibleMsgs, msg) + if resp.CompatibleMsgs[path] == nil { + resp.CompatibleMsgs[path] = make([]string, 0, len(req.MsgNameCrcs)) + } + for _, msg := range req.MsgNameCrcs { + val, ok := messages[msg] + if !ok { + resp.IncompatibleMsgs[path] = append(resp.IncompatibleMsgs[path], msg) + continue + } + if err = ch.CheckCompatiblity(val); err != nil { + resp.IncompatibleMsgs[path] = append(resp.IncompatibleMsgs[path], msg) + } else { + resp.CompatibleMsgs[path] = append(resp.CompatibleMsgs[path], msg) + } } } - - if len(resp.IncompatibleMsgs) > 0 { - return fmt.Errorf("compatibility check failed for messages: %v", resp.IncompatibleMsgs) + for _, messages := range resp.IncompatibleMsgs { + if len(messages) > 0 { + return fmt.Errorf("compatibility check failed for messages: %v", resp.IncompatibleMsgs) + } } return nil -- 2.16.6 From 8d3131f90f71271835e5fed91831565797894614 Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Thu, 3 Dec 2020 14:40:09 +0100 Subject: [PATCH 05/16] Decode message context using the message type only In order to prevent potential future issues, the method returning message based on its ID but ignoring its package was optimized. Change-Id: I12aa2b243f32f38cb3dbc7731613c7ed9fc66539 Signed-off-by: Vladimir Lavor --- codec/msg_codec.go | 12 ++++-------- core/channel.go | 4 ++-- core/request_handler.go | 33 +++++++++------------------------ 3 files changed, 15 insertions(+), 34 deletions(-) diff --git a/codec/msg_codec.go b/codec/msg_codec.go index 6534e7d..227764d 100644 --- a/codec/msg_codec.go +++ b/codec/msg_codec.go @@ -30,8 +30,8 @@ func EncodeMsg(msg api.Message, msgID uint16) (data []byte, err error) { func DecodeMsg(data []byte, msg api.Message) (err error) { return DefaultCodec.DecodeMsg(data, msg) } -func DecodeMsgContext(data []byte, msg api.Message) (context uint32, err error) { - return DefaultCodec.DecodeMsgContext(data, msg) +func DecodeMsgContext(data []byte, msgType api.MessageType) (context uint32, err error) { + return DefaultCodec.DecodeMsgContext(data, msgType) } // MsgCodec provides encoding and decoding functionality of `api.Message` structs into/from @@ -106,12 +106,8 @@ func (*MsgCodec) DecodeMsg(data []byte, msg api.Message) (err error) { return nil } -func (*MsgCodec) DecodeMsgContext(data []byte, msg api.Message) (context uint32, err error) { - if msg == nil { - return 0, errors.New("nil message passed in") - } - - switch msg.GetMessageType() { +func (*MsgCodec) DecodeMsgContext(data []byte, msgType api.MessageType) (context uint32, err error) { + switch msgType { case api.RequestMessage: return binary.BigEndian.Uint32(data[6:10]), nil case api.ReplyMessage: diff --git a/core/channel.go b/core/channel.go index fbb3e59..4cb5761 100644 --- a/core/channel.go +++ b/core/channel.go @@ -37,8 +37,8 @@ type MessageCodec interface { EncodeMsg(msg api.Message, msgID uint16) ([]byte, error) // DecodeMsg decodes binary-encoded data of a message into provided Message structure. DecodeMsg(data []byte, msg api.Message) error - // DecodeMsgContext decodes context from message data. - DecodeMsgContext(data []byte, msg api.Message) (context uint32, err error) + // DecodeMsgContext decodes context from message data and type. + DecodeMsgContext(data []byte, msgType api.MessageType) (context uint32, err error) } // MessageIdentifier provides identification of generated API messages. diff --git a/core/request_handler.go b/core/request_handler.go index f9d972a..29685f6 100644 --- a/core/request_handler.go +++ b/core/request_handler.go @@ -17,7 +17,6 @@ package core import ( "errors" "fmt" - "reflect" "sync/atomic" "time" @@ -210,7 +209,7 @@ func (c *Connection) msgCallback(msgID uint16, data []byte) { return } - msg, err := c.getMessageByID(msgID) + msgType, name, crc, err := c.getMessageDataByID(msgID) if err != nil { log.Warnln(err) return @@ -221,7 +220,7 @@ func (c *Connection) msgCallback(msgID uint16, data []byte) { // - replies that don't have context as first field (comes as zero) // - events that don't have context at all (comes as non zero) // - context, err := c.codec.DecodeMsgContext(data, msg) + context, err := c.codec.DecodeMsgContext(data, msgType) if err != nil { log.WithField("msg_id", msgID).Warnf("Unable to decode message context: %v", err) return @@ -230,14 +229,6 @@ func (c *Connection) msgCallback(msgID uint16, data []byte) { chanID, isMulti, seqNum := unpackRequestContext(context) if log.Level == logger.DebugLevel { // for performance reasons - logrus does some processing even if debugs are disabled - msg = reflect.New(reflect.TypeOf(msg).Elem()).Interface().(api.Message) - - // decode the message - if err = c.codec.DecodeMsg(data, msg); err != nil { - err = fmt.Errorf("decoding message failed: %w", err) - return - } - log.WithFields(logger.Fields{ "context": context, "msg_id": msgID, @@ -245,8 +236,8 @@ func (c *Connection) msgCallback(msgID uint16, data []byte) { "channel": chanID, "is_multi": isMulti, "seq_num": seqNum, - "msg_crc": msg.GetCrcString(), - }).Debugf("<-- govpp RECEIVE: %s %+v", msg.GetMessageName(), msg) + "msg_crc": crc, + }).Debugf("<-- govpp RECEIVE: %s %+v", name) } if context == 0 || c.isNotificationMessage(msgID) { @@ -420,18 +411,12 @@ func compareSeqNumbers(seqNum1, seqNum2 uint16) int { return 1 } -// Returns first message from any package where the message ID matches -// Note: the msg is further used only for its MessageType which is not -// affected by the message's package -func (c *Connection) getMessageByID(msgID uint16) (msg api.Message, err error) { - var ok bool +// Returns message data based on the message ID not depending on the message path +func (c *Connection) getMessageDataByID(msgID uint16) (typ api.MessageType, name, crc string, err error) { for _, msgs := range c.msgMapByPath { - if msg, ok = msgs[msgID]; ok { - break + if msg, ok := msgs[msgID]; ok { + return msg.GetMessageType(), msg.GetMessageName(), msg.GetCrcString(), nil } } - if !ok { - return nil, fmt.Errorf("unknown message received, ID: %d", msgID) - } - return msg, nil + return typ, name, crc, fmt.Errorf("unknown message received, ID: %d", msgID) } -- 2.16.6 From b4a05a1c9728085bfcdd10cc8e6e319926b19ca2 Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Mon, 14 Dec 2020 15:28:53 +0100 Subject: [PATCH 06/16] Added INFO.yaml Change-Id: Id63f1516678cddfef82570958be7e1871ead2c45 Signed-off-by: Vladimir Lavor --- INFO.yaml | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 INFO.yaml diff --git a/INFO.yaml b/INFO.yaml new file mode 100644 index 0000000..6f6a76b --- /dev/null +++ b/INFO.yaml @@ -0,0 +1,56 @@ +--- +project: 'govpp' +project_creation_date: '2017-4-27' +project_category: '' +lifecycle_state: 'Incubation' +project_lead: &govpp_ptl + name: 'Ondrej Fabry' + email: 'ofabry@cisco.com' + id: 'ofabry' +primary_contact: *govpp_ptl +issue_tracking: + type: 'jira' + url: 'https://jira.fd.io/projects/GOVPP' + key: 'GOVPP' +mailing_list: + type: 'groups.io' + url: 'https://lists.fd.io/g/govpp-dev' + tag: '<[sub-project_name]>' +realtime_discussion: + type: '' + server: '' + channel: '' +repositories: + - 'govpp' +committers: + - <<: *govpp_ptl + - name: 'Ondrej Fabry' + company: 'cisco' + email: 'ofabry@cisco.com' + id: 'ofabry' + - name: 'Jan Medved' + company: 'cisco' + email: 'jmedved@cisco.com' + id: 'jmedved' + - name: 'Rastislav Szabo' + company: 'cisco' + email: 'raszabo@cisco.com' + id: 'raszabo' + - name: 'Nikos Bregiannis' + company: 'cisco' + email: 'nbregian@cisco.com' + id: 'nbregian' + - name: 'Vladimir Lavor' + company: 'cisco' + email: 'vlavor@cisco.com' + id: 'vlavor' +tsc: + # yamllint disable rule:line-length + approval: '' + changes: + - type: 'removal' + name: '' + link: '' + - type: 'promotion' + name: 'Vladimir Lavor' + link: 'https://lists.fd.io/g/govpp-dev/topic/77726308#64,https://lists.fd.io/g/govpp-dev/message/69' -- 2.16.6 From 4da29d1fb32a77dd299e84a9ed3a11ddcaa31a3b Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Mon, 21 Dec 2020 17:38:00 +0100 Subject: [PATCH 07/16] Remove socket warnings from client adapters Change-Id: I5e72df39c0e4ba4f0a70d561d7f9b220b4e72a4c Signed-off-by: Ondrej Fabry --- adapter/socketclient/socketclient.go | 119 ++++++++++------------------------- adapter/statsclient/statsclient.go | 59 ++++++++--------- 2 files changed, 58 insertions(+), 120 deletions(-) diff --git a/adapter/socketclient/socketclient.go b/adapter/socketclient/socketclient.go index 4ee319b..4173d7e 100644 --- a/adapter/socketclient/socketclient.go +++ b/adapter/socketclient/socketclient.go @@ -73,29 +73,8 @@ func init() { log = logger.WithField("logger", "govpp/socketclient") } -const socketMissing = ` ------------------------------------------------------------- - No socket file found at: %s - VPP binary API socket file is missing! - - - is VPP running with socket for binapi enabled? - - is the correct socket name configured? - - To enable it add following section to your VPP config: - socksvr { - socket-name /run/vpp/api.sock - } ------------------------------------------------------------- -` - -var warnOnce sync.Once - -func (c *socketClient) printMissingSocketMsg() { - fmt.Fprintf(os.Stderr, socketMissing, c.sockAddr) -} - -type socketClient struct { - sockAddr string +type Client struct { + socketPath string clientName string conn *net.UnixConn @@ -117,12 +96,14 @@ type socketClient struct { wg sync.WaitGroup } -func NewVppClient(sockAddr string) *socketClient { - if sockAddr == "" { - sockAddr = DefaultSocketName +// NewVppClient returns a new Client using socket. +// If socket is empty string DefaultSocketName is used. +func NewVppClient(socket string) *Client { + if socket == "" { + socket = DefaultSocketName } - return &socketClient{ - sockAddr: sockAddr, + return &Client{ + socketPath: socket, clientName: DefaultClientName, connectTimeout: DefaultConnectTimeout, disconnectTimeout: DefaultDisconnectTimeout, @@ -136,61 +117,34 @@ func NewVppClient(sockAddr string) *socketClient { } // SetClientName sets a client name used for identification. -func (c *socketClient) SetClientName(name string) { +func (c *Client) SetClientName(name string) { c.clientName = name } // SetConnectTimeout sets timeout used during connecting. -func (c *socketClient) SetConnectTimeout(t time.Duration) { +func (c *Client) SetConnectTimeout(t time.Duration) { c.connectTimeout = t } // SetDisconnectTimeout sets timeout used during disconnecting. -func (c *socketClient) SetDisconnectTimeout(t time.Duration) { +func (c *Client) SetDisconnectTimeout(t time.Duration) { c.disconnectTimeout = t } -func (c *socketClient) SetMsgCallback(cb adapter.MsgCallback) { +func (c *Client) SetMsgCallback(cb adapter.MsgCallback) { log.Debug("SetMsgCallback") c.msgCallback = cb } -const legacySocketName = "/run/vpp-api.sock" - -func (c *socketClient) checkLegacySocket() bool { - if c.sockAddr == legacySocketName { - return false - } - log.Debugf("checking legacy socket: %s", legacySocketName) - // check if socket exists - if _, err := os.Stat(c.sockAddr); err == nil { - return false // socket exists - } else if !os.IsNotExist(err) { - return false // some other error occurred - } - // check if legacy socket exists - if _, err := os.Stat(legacySocketName); err == nil { - // legacy socket exists, update sockAddr - c.sockAddr = legacySocketName - return true - } - // no socket socket found - return false -} - // WaitReady checks socket file existence and waits for it if necessary -func (c *socketClient) WaitReady() error { +func (c *Client) WaitReady() error { // check if socket already exists - if _, err := os.Stat(c.sockAddr); err == nil { + if _, err := os.Stat(c.socketPath); err == nil { return nil // socket exists, we are ready } else if !os.IsNotExist(err) { return err // some other error occurred } - if c.checkLegacySocket() { - return nil - } - // socket does not exist, watch for it watcher, err := fsnotify.NewWatcher() if err != nil { @@ -203,7 +157,7 @@ func (c *socketClient) WaitReady() error { }() // start directory watcher - if err := watcher.Add(filepath.Dir(c.sockAddr)); err != nil { + if err := watcher.Add(filepath.Dir(c.socketPath)); err != nil { return err } @@ -211,17 +165,14 @@ func (c *socketClient) WaitReady() error { for { select { case <-timeout.C: - if c.checkLegacySocket() { - return nil - } - return fmt.Errorf("timeout waiting (%s) for socket file: %s", MaxWaitReady, c.sockAddr) + return fmt.Errorf("timeout waiting (%s) for socket file: %s", MaxWaitReady, c.socketPath) case e := <-watcher.Errors: return e case ev := <-watcher.Events: log.Debugf("watcher event: %+v", ev) - if ev.Name == c.sockAddr && (ev.Op&fsnotify.Create) == fsnotify.Create { + if ev.Name == c.socketPath && (ev.Op&fsnotify.Create) == fsnotify.Create { // socket created, we are ready return nil } @@ -229,18 +180,15 @@ func (c *socketClient) WaitReady() error { } } -func (c *socketClient) Connect() error { - c.checkLegacySocket() - +func (c *Client) Connect() error { // check if socket exists - if _, err := os.Stat(c.sockAddr); os.IsNotExist(err) { - warnOnce.Do(c.printMissingSocketMsg) - return fmt.Errorf("VPP API socket file %s does not exist", c.sockAddr) + if _, err := os.Stat(c.socketPath); os.IsNotExist(err) { + return fmt.Errorf("VPP API socket file %s does not exist", c.socketPath) } else if err != nil { return fmt.Errorf("VPP API socket error: %v", err) } - if err := c.connect(c.sockAddr); err != nil { + if err := c.connect(c.socketPath); err != nil { return err } @@ -256,7 +204,7 @@ func (c *socketClient) Connect() error { return nil } -func (c *socketClient) Disconnect() error { +func (c *Client) Disconnect() error { if c.conn == nil { return nil } @@ -273,7 +221,6 @@ func (c *socketClient) Disconnect() error { // Don't bother sending a vl_api_sockclnt_delete_t message, // just close the socket. - if err := c.disconnect(); err != nil { return err } @@ -283,10 +230,10 @@ func (c *socketClient) Disconnect() error { const defaultBufferSize = 4096 -func (c *socketClient) connect(sockAddr string) error { +func (c *Client) connect(sockAddr string) error { addr := &net.UnixAddr{Name: sockAddr, Net: "unix"} - log.Debugf("Connecting to: %v", c.sockAddr) + log.Debugf("Connecting to: %v", c.socketPath) conn, err := net.DialUnix("unix", nil, addr) if err != nil { @@ -311,7 +258,7 @@ func (c *socketClient) connect(sockAddr string) error { return nil } -func (c *socketClient) disconnect() error { +func (c *Client) disconnect() error { log.Debugf("Closing socket") if err := c.conn.Close(); err != nil { log.Debugln("Closing socket failed:", err) @@ -326,7 +273,7 @@ const ( deleteMsgContext = byte(124) ) -func (c *socketClient) open() error { +func (c *Client) open() error { var msgCodec = codec.DefaultCodec // Request socket client create @@ -379,7 +326,7 @@ func (c *socketClient) open() error { return nil } -func (c *socketClient) GetMsgID(msgName string, msgCrc string) (uint16, error) { +func (c *Client) GetMsgID(msgName string, msgCrc string) (uint16, error) { if msgID, ok := c.msgTable[msgName+"_"+msgCrc]; ok { return msgID, nil } @@ -389,7 +336,7 @@ func (c *socketClient) GetMsgID(msgName string, msgCrc string) (uint16, error) { } } -func (c *socketClient) SendMsg(context uint32, data []byte) error { +func (c *Client) SendMsg(context uint32, data []byte) error { if len(data) < 10 { return fmt.Errorf("invalid message data, length must be at least 10 bytes") } @@ -423,7 +370,7 @@ func setMsgRequestHeader(data []byte, clientIndex, context uint32) { binary.BigEndian.PutUint32(data[6:10], context) } -func (c *socketClient) writeMsg(msg []byte) error { +func (c *Client) writeMsg(msg []byte) error { // we lock to prevent mixing multiple message writes c.writeMu.Lock() defer c.writeMu.Unlock() @@ -482,7 +429,7 @@ func writeMsgData(w io.Writer, msg []byte, writerSize int) error { return nil } -func (c *socketClient) readerLoop() { +func (c *Client) readerLoop() { defer c.wg.Done() defer log.Debugf("reader loop done") @@ -528,7 +475,7 @@ func getMsgReplyHeader(msg []byte) (msgID uint16, context uint32) { return } -func (c *socketClient) readMsgTimeout(buf []byte, timeout time.Duration) ([]byte, error) { +func (c *Client) readMsgTimeout(buf []byte, timeout time.Duration) ([]byte, error) { // set read deadline readDeadline := time.Now().Add(timeout) if err := c.conn.SetReadDeadline(readDeadline); err != nil { @@ -549,7 +496,7 @@ func (c *socketClient) readMsgTimeout(buf []byte, timeout time.Duration) ([]byte return msgReply, nil } -func (c *socketClient) readMsg(buf []byte) ([]byte, error) { +func (c *Client) readMsg(buf []byte) ([]byte, error) { log.Debug("reading msg..") header := c.headerPool.Get().([]byte) diff --git a/adapter/statsclient/statsclient.go b/adapter/statsclient/statsclient.go index 8693410..9470275 100644 --- a/adapter/statsclient/statsclient.go +++ b/adapter/statsclient/statsclient.go @@ -24,10 +24,11 @@ import ( "syscall" "time" - "git.fd.io/govpp.git/adapter" "github.com/fsnotify/fsnotify" "github.com/ftrvxmtrx/fd" logger "github.com/sirupsen/logrus" + + "git.fd.io/govpp.git/adapter" ) const ( @@ -35,20 +36,6 @@ const ( DefaultSocketName = adapter.DefaultStatsSocket ) -const socketMissing = ` ------------------------------------------------------------- - VPP stats socket file %s is missing! - - - is VPP running with stats segment enabled? - - is the correct socket name configured? - - To enable it add following section to your VPP config: - statseg { - socket-name /run/vpp/stats.sock - } ------------------------------------------------------------- -` - var ( // Debug is global variable that determines debug mode Debug = os.Getenv("DEBUG_GOVPP_STATS") != "" @@ -77,7 +64,8 @@ var _ adapter.StatsAPI = (*StatsClient)(nil) // StatsClient is the pure Go implementation for VPP stats API. type StatsClient struct { - sockAddr string + socketPath string + headerData []byte isConnected bool @@ -87,13 +75,14 @@ type StatsClient struct { statSegment } -// NewStatsClient returns new VPP stats API client. -func NewStatsClient(sockAddr string) *StatsClient { - if sockAddr == "" { - sockAddr = DefaultSocketName +// NewStatsClient returns a new StatsClient using socket. +// If socket is empty string DefaultSocketName is used. +func NewStatsClient(socket string) *StatsClient { + if socket == "" { + socket = DefaultSocketName } return &StatsClient{ - sockAddr: sockAddr, + socketPath: socket, } } @@ -103,20 +92,25 @@ func (sc *StatsClient) Connect() (err error) { if sc.isConnected { return fmt.Errorf("already connected") } - if err := sc.validate(); err != nil { + if err := sc.checkSocketValid(); err != nil { return err } sc.done = make(chan struct{}) - sc.monitorSocket() if sc.statSegment, err = sc.connect(); err != nil { return err } + sc.monitorSocket() + sc.isConnected = true return nil } // Disconnect from the socket, unmap shared memory and terminate // socket monitor func (sc *StatsClient) Disconnect() error { + if !sc.isConnected { + return nil // not connected + } + sc.isConnected = false close(sc.done) return sc.disconnect() } @@ -287,11 +281,9 @@ func (sc *StatsClient) UpdateDir(dir *adapter.StatDir) (err error) { return nil } -// validate file presence by retrieving its file info -func (sc *StatsClient) validate() error { - if _, err := os.Stat(sc.sockAddr); os.IsNotExist(err) { - fmt.Fprintf(os.Stderr, socketMissing, sc.sockAddr) - return fmt.Errorf("stats socket file %s does not exist", sc.sockAddr) +func (sc *StatsClient) checkSocketValid() error { + if _, err := os.Stat(sc.socketPath); os.IsNotExist(err) { + return fmt.Errorf("stats socket file %s does not exist", sc.socketPath) } else if err != nil { return fmt.Errorf("stats socket error: %v", err) } @@ -303,7 +295,7 @@ func (sc *StatsClient) validate() error { func (sc *StatsClient) connect() (ss statSegment, err error) { addr := net.UnixAddr{ Net: "unixpacket", - Name: sc.sockAddr, + Name: sc.socketPath, } Log.Debugf("connecting to: %v", addr) @@ -357,7 +349,7 @@ func (sc *StatsClient) connect() (ss statSegment, err error) { return nil, fmt.Errorf("stat segment version is not supported: %v (min: %v, max: %v)", version, minVersion, maxVersion) } - sc.isConnected = true + return ss, nil } @@ -367,7 +359,7 @@ func (sc *StatsClient) reconnect() (err error) { if err = sc.disconnect(); err != nil { return fmt.Errorf("error disconnecting socket: %v", err) } - if err = sc.validate(); err != nil { + if err = sc.checkSocketValid(); err != nil { return fmt.Errorf("error validating socket: %v", err) } if sc.statSegment, err = sc.connect(); err != nil { @@ -378,7 +370,6 @@ func (sc *StatsClient) reconnect() (err error) { // disconnect unmaps socket data from the memory and resets the header func (sc *StatsClient) disconnect() error { - sc.isConnected = false if sc.headerData == nil { return nil } @@ -408,7 +399,7 @@ func (sc *StatsClient) monitorSocket() { Log.Errorf("error occurred during socket reconnect: %v", err) } // path must be re-added to the watcher - if err = watcher.Add(sc.sockAddr); err != nil { + if err = watcher.Add(sc.socketPath); err != nil { Log.Errorf("failed to add socket address to the watcher: %v", err) } } @@ -422,7 +413,7 @@ func (sc *StatsClient) monitorSocket() { } }() - if err := watcher.Add(sc.sockAddr); err != nil { + if err := watcher.Add(sc.socketPath); err != nil { Log.Errorf("failed to add socket address to the watcher: %v", err) } } -- 2.16.6 From c0c73d34a7f5eae44e7c9230ddccc0cfcb201084 Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Tue, 22 Dec 2020 15:35:31 +0100 Subject: [PATCH 08/16] Rework test for binary API union sizes The test now generates various unions from union.api.json and tests correct sizes of generated types. Change-Id: Ifaf18a8ce650e71a8ca8b2d5cfb9d7eed2d757c6 Signed-off-by: Vladimir Lavor --- binapigen/generator_test.go | 86 +++++------- binapigen/vppapi/testdata/union.api.json | 231 +++++++++++++++++++++++++++++++ binapigen/vppapi/vppapi_test.go | 2 +- core/request_handler.go | 2 +- 4 files changed, 269 insertions(+), 52 deletions(-) create mode 100644 binapigen/vppapi/testdata/union.api.json diff --git a/binapigen/generator_test.go b/binapigen/generator_test.go index 7f334a9..3e654d7 100644 --- a/binapigen/generator_test.go +++ b/binapigen/generator_test.go @@ -15,9 +15,12 @@ package binapigen import ( + "bufio" "fmt" "git.fd.io/govpp.git/binapigen/vppapi" . "github.com/onsi/gomega" + "os" + "strings" "testing" ) @@ -58,57 +61,40 @@ func TestBinapiTypeSizes(t *testing.T) { func TestBinapiUnionSizes(t *testing.T) { RegisterTestingT(t) - tests := []struct { - testName string - input *Union - expsize int - }{ - {testName: "union_alias", input: typeTestData{ - typ: "union", fields: []*typeTestData{{typ: "alias", value: U16}, - }}.getUnion("union1"), expsize: 2}, - {testName: "union_enum", input: typeTestData{ - typ: "union", fields: []*typeTestData{{typ: "enum", value: U32}, - }}.getUnion("union2"), expsize: 4}, - {testName: "union_struct", input: typeTestData{ - typ: "union", fields: []*typeTestData{ - {typ: "struct", fields: []*typeTestData{{value: U8}, {value: U16}, {value: U32}}}, - }}.getUnion("union3"), expsize: 7}, - {testName: "union_structs", input: typeTestData{ - typ: "union", fields: []*typeTestData{ - {typ: "struct", fields: []*typeTestData{{value: U8}, {value: BOOL}}}, - {typ: "struct", fields: []*typeTestData{{value: U16}, {value: U32}}}, - {typ: "struct", fields: []*typeTestData{{value: U32}, {value: U64}}}, - }}.getUnion("union4"), expsize: 12}, - {testName: "union_unions", input: typeTestData{ - typ: "union", fields: []*typeTestData{ - {typ: "union", fields: []*typeTestData{ - {typ: "struct", fields: []*typeTestData{{value: STRING}}}, - }}, - {typ: "union", fields: []*typeTestData{ - {typ: "struct", fields: []*typeTestData{{value: U32}}}, - }}, - {typ: "union", fields: []*typeTestData{ - {typ: "struct", fields: []*typeTestData{{value: U64}}}, - }}, - }}.getUnion("union5"), expsize: 8}, - {testName: "union_combined", input: typeTestData{ - typ: "union", fields: []*typeTestData{ - {typ: "alias", value: U8}, - {typ: "enum", value: U16}, - {typ: "struct", fields: []*typeTestData{{value: U8}, {value: U16}, {value: U32}}}, // <- - {typ: "union", fields: []*typeTestData{ - {typ: "alias", value: U16}, - {typ: "enum", value: U16}, - {typ: "struct", fields: []*typeTestData{{value: U32}}}, - }}, - }}.getUnion("union6"), expsize: 7}, - } - for _, test := range tests { - t.Run(test.testName, func(t *testing.T) { - size := getUnionSize(test.input) - Expect(size).To(Equal(test.expsize)) - }) + + // order of the union sizes in file generated from union.api.json + var sizes = []int{16, 4, 32, 16, 64, 111} + + // remove directory created during test + defer func() { + err := os.RemoveAll(testOutputDir) + Expect(err).ToNot(HaveOccurred()) + }() + + err := GenerateFromFile("vppapi/testdata/union.api.json", Options{OutputDir: testOutputDir}) + Expect(err).ShouldNot(HaveOccurred()) + + file, err := os.Open(testOutputDir + "/union/union.ba.go") + Expect(err).ShouldNot(HaveOccurred()) + defer func() { + err := file.Close() + Expect(err).ToNot(HaveOccurred()) + }() + + // the generated line with union size is in format XXX_UnionData []byte + // the prefix identifies these lines (the starting tab is important) + prefix := fmt.Sprintf("\t%s", "XXX_UnionData [") + + index := 0 + scanner := bufio.NewScanner(file) + for scanner.Scan() { + if strings.HasPrefix(scanner.Text(), prefix) { + Expect(scanner.Text()).To(Equal(prefix + fmt.Sprintf("%d]byte", sizes[index]))) + index++ + } } + // ensure all union sizes were found and tested + Expect(index).To(Equal(len(sizes))) } // Typed data used for union size evaluation testing. diff --git a/binapigen/vppapi/testdata/union.api.json b/binapigen/vppapi/testdata/union.api.json new file mode 100644 index 0000000..0811f22 --- /dev/null +++ b/binapigen/vppapi/testdata/union.api.json @@ -0,0 +1,231 @@ +{ + "services": [], + "vl_api_version": "0x1db2ece9", + "enums": [ + [ + "enum1", + [ + "ENUM_1_VALUE_1", + 1 + ], + [ + "ENUM_1_VALUE_2", + 2 + ], + { + "enumtype": "u16" + } + ], + [ + "enum2", + [ + "ENUM_2_VALUE_1", + 10 + ], + [ + "ENUM_2_VALUE_2", + 20 + ], + { + "enumtype": "u32" + } + ] + ], + "messages": [], + "types": [ + [ + "type1", + [ + "u8", + "field1", + 16 + ], + [ + "u8", + "field2", + 16 + ] + ], + [ + "type2", + [ + "u16", + "field1" + ], + [ + "u32", + "field2" + ], + [ + "u32", + "field3" + ] + ], + [ + "type3", + [ + "u8", + "field1", + 64 + ] + ], + [ + "type4", + [ + "u8", + "field1" + ], + [ + "u8", + "field2", + 16 + ] + ], + [ + "type5", + [ + "u32", + "field1" + ], + [ + "union5", + "field2" + ] + ], + [ + "type6", + [ + "u16", + "field1" + ], + [ + "u32", + "field2" + ], + [ + "type4", + "field3" + ], + [ + "u16", + "field4" + ], + [ + "u32", + "field5" + ], + [ + "u32", + "field6" + ] + ], + [ + "complex_type", + [ + "u32", + "field1" + ], + [ + "u8", + "field2" + ], + [ + "u8", + "field3" + ], + [ + "u32", + "field4" + ], + [ + "type5", + "field5" + ], + [ + "type6", + "field6" + ] + ] + ], + "unions": [ + [ + "union1", + [ + "vl_api_alias1_t", + "alias1" + ], + [ + "vl_api_alias2_t", + "alias2" + ] + ], + [ + "union2", + [ + "vl_api_enum1_t", + "enum1" + ], + [ + "vl_api_enum2_t", + "enum2" + ] + ], + [ + "union3", + [ + "vl_api_type1_t", + "type1" + ], + [ + "vl_api_type2_t", + "type2" + ] + ], + [ + "union4", + [ + "vl_api_union1_t", + "union1" + ], + [ + "vl_api_union2_t", + "union2" + ] + ], + [ + "union5", + [ + "vl_api_type1_t", + "type1" + ], + [ + "vl_api_type3_t", + "type3" + ] + ], + [ + "union6", + [ + "vl_api_type1_t", + "type1" + ], + [ + "vl_api_complex_type_t", + "type3" + ] + ] + ], + "aliases": { + "alias1": { + "type": "u8", + "length": 4 + }, + "alias2": { + "type": "u8", + "length": 16 + }, + "alias3": { + "type": "u32" + } + } +} diff --git a/binapigen/vppapi/vppapi_test.go b/binapigen/vppapi/vppapi_test.go index 027cc1f..a555d9f 100644 --- a/binapigen/vppapi/vppapi_test.go +++ b/binapigen/vppapi/vppapi_test.go @@ -27,7 +27,7 @@ func TestGetInputFiles(t *testing.T) { result, err := FindFiles("testdata", 1) Expect(err).ShouldNot(HaveOccurred()) - Expect(result).To(HaveLen(5)) + Expect(result).To(HaveLen(6)) for _, file := range result { Expect(file).To(BeAnExistingFile()) } diff --git a/core/request_handler.go b/core/request_handler.go index 29685f6..95bd924 100644 --- a/core/request_handler.go +++ b/core/request_handler.go @@ -237,7 +237,7 @@ func (c *Connection) msgCallback(msgID uint16, data []byte) { "is_multi": isMulti, "seq_num": seqNum, "msg_crc": crc, - }).Debugf("<-- govpp RECEIVE: %s %+v", name) + }).Debugf("<-- govpp RECEIVE: %s", name) } if context == 0 || c.isNotificationMessage(msgID) { -- 2.16.6 From 4c1cccf48cd144414c7233f167087aff770ef67b Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Mon, 1 Feb 2021 14:37:26 +0100 Subject: [PATCH 09/16] binapigen: added enumflags type Change-Id: I2f46504bd05862e415dab518fad349d08aedf919 Signed-off-by: Vladimir Lavor --- binapigen/binapigen.go | 10 +- binapigen/generate.go | 2 +- binapigen/generate_test.go | 17 +- binapigen/vppapi.go | 26 + binapigen/vppapi/api_schema.go | 9 +- binapigen/vppapi/parse_json.go | 15 + binapigen/vppapi/testdata/ip.api.json | 3540 +++++++++++++++++++++++---------- cmd/govpp/main.go | 4 +- 8 files changed, 2509 insertions(+), 1114 deletions(-) diff --git a/binapigen/binapigen.go b/binapigen/binapigen.go index de6a804..59aa84a 100644 --- a/binapigen/binapigen.go +++ b/binapigen/binapigen.go @@ -71,7 +71,10 @@ func newFile(gen *Generator, apifile *vppapi.File, packageName GoPackageName, im } for _, enumType := range apifile.EnumTypes { - file.Enums = append(file.Enums, newEnum(gen, file, enumType)) + file.Enums = append(file.Enums, newEnum(gen, file, enumType, false)) + } + for _, enumflagType := range apifile.EnumflagTypes { + file.Enums = append(file.Enums, newEnum(gen, file, enumflagType, true)) } for _, aliasType := range apifile.AliasTypes { file.Aliases = append(file.Aliases, newAlias(gen, file, aliasType)) @@ -167,15 +170,18 @@ type Enum struct { vppapi.EnumType GoIdent + + IsFlag bool } -func newEnum(gen *Generator, file *File, apitype vppapi.EnumType) *Enum { +func newEnum(gen *Generator, file *File, apitype vppapi.EnumType, isFlag bool) *Enum { typ := &Enum{ EnumType: apitype, GoIdent: GoIdent{ GoName: camelCaseName(apitype.Name), GoImportPath: file.GoImportPath, }, + IsFlag: isFlag, } gen.enumsByName[typ.Name] = typ return typ diff --git a/binapigen/generate.go b/binapigen/generate.go index bf6df81..a2f941a 100644 --- a/binapigen/generate.go +++ b/binapigen/generate.go @@ -178,7 +178,7 @@ func genEnum(g *GenFile, enum *Enum) { g.P(")") g.P() - if isEnumFlag(enum) { + if enum.IsFlag || isEnumFlag(enum) { size := BaseTypeSizes[enum.Type] * 8 g.P("func (x ", enum.GoName, ") String() string {") g.P(" s, ok := ", enum.GoName, "_name[", gotype, "(x)]") diff --git a/binapigen/generate_test.go b/binapigen/generate_test.go index 2fa5dc6..b1d4d70 100644 --- a/binapigen/generate_test.go +++ b/binapigen/generate_test.go @@ -47,7 +47,7 @@ func GenerateFromFile(file string, opts Options) error { return nil } -func TestGenerateFromFile(t *testing.T) { +func TestGenerateFromFileACL(t *testing.T) { RegisterTestingT(t) // remove directory created during test @@ -62,6 +62,21 @@ func TestGenerateFromFile(t *testing.T) { Expect(fileInfo.Name()).To(BeEquivalentTo("acl.ba.go")) } +func TestGenerateFromFileIP(t *testing.T) { + RegisterTestingT(t) + + // remove directory created during test + defer os.RemoveAll(testOutputDir) + + opts := Options{OutputDir: testOutputDir} + err := GenerateFromFile("vppapi/testdata/ip.api.json", opts) + Expect(err).ShouldNot(HaveOccurred()) + fileInfo, err := os.Stat(testOutputDir + "/ip/ip.ba.go") + Expect(err).ShouldNot(HaveOccurred()) + Expect(fileInfo.IsDir()).To(BeFalse()) + Expect(fileInfo.Name()).To(BeEquivalentTo("ip.ba.go")) +} + func TestGenerateFromFileInputError(t *testing.T) { RegisterTestingT(t) diff --git a/binapigen/vppapi.go b/binapigen/vppapi.go index 7388ad5..c18d7fb 100644 --- a/binapigen/vppapi.go +++ b/binapigen/vppapi.go @@ -28,6 +28,9 @@ func SortFileObjectsByName(file *vppapi.File) { sort.SliceStable(file.EnumTypes, func(i, j int) bool { return file.EnumTypes[i].Name < file.EnumTypes[j].Name }) + sort.SliceStable(file.EnumflagTypes, func(i, j int) bool { + return file.EnumflagTypes[i].Name < file.EnumflagTypes[j].Name + }) sort.Slice(file.AliasTypes, func(i, j int) bool { return file.AliasTypes[i].Name < file.AliasTypes[j].Name }) @@ -151,6 +154,22 @@ func ListImportedTypes(apifiles []*vppapi.File, file *vppapi.File) []string { } } } + for _, t := range file.EnumflagTypes { + var imported bool + for _, imp := range typeFiles { + for _, at := range imp.EnumflagTypes { + if at.Name != t.Name { + continue + } + importedTypes = append(importedTypes, t.Name) + imported = true + break + } + if imported { + break + } + } + } for _, t := range file.UnionTypes { var imported bool for _, imp := range typeFiles { @@ -186,6 +205,12 @@ func RemoveImportedTypes(apifiles []*vppapi.File, apifile *vppapi.File) { enums = append(enums, enumType) } } + var enumflags []vppapi.EnumType + for _, enumflagType := range apifile.EnumflagTypes { + if !isImportedType(enumflagType.Name) { + enumflags = append(enumflags, enumflagType) + } + } var aliases []vppapi.AliasType for _, aliasType := range apifile.AliasTypes { if !isImportedType(aliasType.Name) { @@ -205,6 +230,7 @@ func RemoveImportedTypes(apifiles []*vppapi.File, apifile *vppapi.File) { } } apifile.EnumTypes = enums + apifile.EnumflagTypes = enumflags apifile.AliasTypes = aliases apifile.StructTypes = structs apifile.UnionTypes = unions diff --git a/binapigen/vppapi/api_schema.go b/binapigen/vppapi/api_schema.go index 7eceab3..e1c180e 100644 --- a/binapigen/vppapi/api_schema.go +++ b/binapigen/vppapi/api_schema.go @@ -24,10 +24,11 @@ type ( Options map[string]string `json:",omitempty"` Imports []string `json:",omitempty"` - AliasTypes []AliasType `json:",omitempty"` - EnumTypes []EnumType `json:",omitempty"` - StructTypes []StructType `json:",omitempty"` - UnionTypes []UnionType `json:",omitempty"` + AliasTypes []AliasType `json:",omitempty"` + EnumTypes []EnumType `json:",omitempty"` + EnumflagTypes []EnumType `json:",omitempty"` + StructTypes []StructType `json:",omitempty"` + UnionTypes []UnionType `json:",omitempty"` Messages []Message `json:",omitempty"` Service *Service `json:",omitempty"` diff --git a/binapigen/vppapi/parse_json.go b/binapigen/vppapi/parse_json.go index d14865c..a2ca257 100644 --- a/binapigen/vppapi/parse_json.go +++ b/binapigen/vppapi/parse_json.go @@ -41,6 +41,7 @@ const ( fileMessages = "messages" fileUnions = "unions" fileEnums = "enums" + fileEnumflags = "enumflags" fileAliases = "aliases" fileServices = "services" fileImports = "imports" @@ -129,6 +130,20 @@ func parseJSON(data []byte) (module *File, err error) { module.EnumTypes = append(module.EnumTypes, *enum) } + // parse enumflags types + enumflagsNode := jsonRoot.Map(fileEnumflags) + module.EnumflagTypes = make([]EnumType, 0) + for i := 0; i < enumflagsNode.Len(); i++ { + enumflag, err := parseEnum(enumflagsNode.At(i)) + if err != nil { + return nil, err + } + if exists(enumflag.Name) { + continue + } + module.EnumflagTypes = append(module.EnumflagTypes, *enumflag) + } + // parse alias types aliasesNode := jsonRoot.Map(fileAliases) if aliasesNode.GetType() == jsongo.TypeMap { diff --git a/binapigen/vppapi/testdata/ip.api.json b/binapigen/vppapi/testdata/ip.api.json index 530b6d6..32be996 100644 --- a/binapigen/vppapi/testdata/ip.api.json +++ b/binapigen/vppapi/testdata/ip.api.json @@ -1,190 +1,1137 @@ { - "services": [ - { - "ip_source_and_port_range_check_add_del": { - "reply": "ip_source_and_port_range_check_add_del_reply" - } - }, - { - "ip6_fib_dump": { - "reply": "ip6_fib_details", - "stream": true - } - }, - { - "want_ip6_nd_events": { - "reply": "want_ip6_nd_events_reply" - } - }, - { - "ip_punt_police": { - "reply": "ip_punt_police_reply" - } - }, - { - "set_arp_neighbor_limit": { - "reply": "set_arp_neighbor_limit_reply" - } - }, - { - "ip6nd_proxy_add_del": { - "reply": "ip6nd_proxy_add_del_reply" - } - }, - { - "ioam_disable": { - "reply": "ioam_disable_reply" - } - }, - { - "ip_table_add_del": { - "reply": "ip_table_add_del_reply" - } - }, - { - "ip_neighbor_dump": { - "reply": "ip_neighbor_details", - "stream": true - } - }, - { - "ip4_arp_event": { - "reply": null + "types": [ + [ + "address", + [ + "vl_api_address_family_t", + "af" + ], + [ + "vl_api_address_union_t", + "un" + ] + ], + [ + "prefix", + [ + "vl_api_address_t", + "address" + ], + [ + "u8", + "len" + ] + ], + [ + "ip4_address_and_mask", + [ + "vl_api_ip4_address_t", + "addr" + ], + [ + "vl_api_ip4_address_t", + "mask" + ] + ], + [ + "ip6_address_and_mask", + [ + "vl_api_ip6_address_t", + "addr" + ], + [ + "vl_api_ip6_address_t", + "mask" + ] + ], + [ + "mprefix", + [ + "vl_api_address_family_t", + "af" + ], + [ + "u16", + "grp_address_length" + ], + [ + "vl_api_address_union_t", + "grp_address" + ], + [ + "vl_api_address_union_t", + "src_address" + ] + ], + [ + "ip6_prefix", + [ + "vl_api_ip6_address_t", + "address" + ], + [ + "u8", + "len" + ] + ], + [ + "ip4_prefix", + [ + "vl_api_ip4_address_t", + "address" + ], + [ + "u8", + "len" + ] + ], + [ + "prefix_matcher", + [ + "u8", + "le" + ], + [ + "u8", + "ge" + ] + ], + [ + "fib_mpls_label", + [ + "u8", + "is_uniform" + ], + [ + "u32", + "label" + ], + [ + "u8", + "ttl" + ], + [ + "u8", + "exp" + ] + ], + [ + "fib_path_nh", + [ + "vl_api_address_union_t", + "address" + ], + [ + "u32", + "via_label" + ], + [ + "u32", + "obj_id" + ], + [ + "u32", + "classify_table_index" + ] + ], + [ + "fib_path", + [ + "u32", + "sw_if_index" + ], + [ + "u32", + "table_id" + ], + [ + "u32", + "rpf_id" + ], + [ + "u8", + "weight" + ], + [ + "u8", + "preference" + ], + [ + "vl_api_fib_path_type_t", + "type" + ], + [ + "vl_api_fib_path_flags_t", + "flags" + ], + [ + "vl_api_fib_path_nh_proto_t", + "proto" + ], + [ + "vl_api_fib_path_nh_t", + "nh" + ], + [ + "u8", + "n_labels" + ], + [ + "vl_api_fib_mpls_label_t", + "label_stack", + 16 + ] + ], + [ + "address", + [ + "vl_api_address_family_t", + "af" + ], + [ + "vl_api_address_union_t", + "un" + ] + ], + [ + "prefix", + [ + "vl_api_address_t", + "address" + ], + [ + "u8", + "len" + ] + ], + [ + "ip4_address_and_mask", + [ + "vl_api_ip4_address_t", + "addr" + ], + [ + "vl_api_ip4_address_t", + "mask" + ] + ], + [ + "ip6_address_and_mask", + [ + "vl_api_ip6_address_t", + "addr" + ], + [ + "vl_api_ip6_address_t", + "mask" + ] + ], + [ + "mprefix", + [ + "vl_api_address_family_t", + "af" + ], + [ + "u16", + "grp_address_length" + ], + [ + "vl_api_address_union_t", + "grp_address" + ], + [ + "vl_api_address_union_t", + "src_address" + ] + ], + [ + "ip6_prefix", + [ + "vl_api_ip6_address_t", + "address" + ], + [ + "u8", + "len" + ] + ], + [ + "ip4_prefix", + [ + "vl_api_ip4_address_t", + "address" + ], + [ + "u8", + "len" + ] + ], + [ + "prefix_matcher", + [ + "u8", + "le" + ], + [ + "u8", + "ge" + ] + ], + [ + "fib_mpls_label", + [ + "u8", + "is_uniform" + ], + [ + "u32", + "label" + ], + [ + "u8", + "ttl" + ], + [ + "u8", + "exp" + ] + ], + [ + "fib_path_nh", + [ + "vl_api_address_union_t", + "address" + ], + [ + "u32", + "via_label" + ], + [ + "u32", + "obj_id" + ], + [ + "u32", + "classify_table_index" + ] + ], + [ + "fib_path", + [ + "u32", + "sw_if_index" + ], + [ + "u32", + "table_id" + ], + [ + "u32", + "rpf_id" + ], + [ + "u8", + "weight" + ], + [ + "u8", + "preference" + ], + [ + "vl_api_fib_path_type_t", + "type" + ], + [ + "vl_api_fib_path_flags_t", + "flags" + ], + [ + "vl_api_fib_path_nh_proto_t", + "proto" + ], + [ + "vl_api_fib_path_nh_t", + "nh" + ], + [ + "u8", + "n_labels" + ], + [ + "vl_api_fib_mpls_label_t", + "label_stack", + 16 + ] + ], + [ + "address", + [ + "vl_api_address_family_t", + "af" + ], + [ + "vl_api_address_union_t", + "un" + ] + ], + [ + "prefix", + [ + "vl_api_address_t", + "address" + ], + [ + "u8", + "len" + ] + ], + [ + "ip4_address_and_mask", + [ + "vl_api_ip4_address_t", + "addr" + ], + [ + "vl_api_ip4_address_t", + "mask" + ] + ], + [ + "ip6_address_and_mask", + [ + "vl_api_ip6_address_t", + "addr" + ], + [ + "vl_api_ip6_address_t", + "mask" + ] + ], + [ + "mprefix", + [ + "vl_api_address_family_t", + "af" + ], + [ + "u16", + "grp_address_length" + ], + [ + "vl_api_address_union_t", + "grp_address" + ], + [ + "vl_api_address_union_t", + "src_address" + ] + ], + [ + "ip6_prefix", + [ + "vl_api_ip6_address_t", + "address" + ], + [ + "u8", + "len" + ] + ], + [ + "ip4_prefix", + [ + "vl_api_ip4_address_t", + "address" + ], + [ + "u8", + "len" + ] + ], + [ + "prefix_matcher", + [ + "u8", + "le" + ], + [ + "u8", + "ge" + ] + ], + [ + "mfib_path", + [ + "vl_api_mfib_itf_flags_t", + "itf_flags" + ], + [ + "vl_api_fib_path_t", + "path" + ] + ], + [ + "ip_table", + [ + "u32", + "table_id" + ], + [ + "bool", + "is_ip6" + ], + [ + "string", + "name", + 64 + ] + ], + [ + "ip_route", + [ + "u32", + "table_id" + ], + [ + "u32", + "stats_index" + ], + [ + "vl_api_prefix_t", + "prefix" + ], + [ + "u8", + "n_paths" + ], + [ + "vl_api_fib_path_t", + "paths", + 0, + "n_paths" + ] + ], + [ + "ip_mroute", + [ + "u32", + "table_id" + ], + [ + "vl_api_mfib_entry_flags_t", + "entry_flags" + ], + [ + "u32", + "rpf_id" + ], + [ + "vl_api_mprefix_t", + "prefix" + ], + [ + "u8", + "n_paths" + ], + [ + "vl_api_mfib_path_t", + "paths", + 0, + "n_paths" + ] + ], + [ + "punt_redirect", + [ + "vl_api_interface_index_t", + "rx_sw_if_index" + ], + [ + "vl_api_interface_index_t", + "tx_sw_if_index" + ], + [ + "vl_api_address_t", + "nh" + ] + ] + ], + "messages": [ + [ + "ip_table_add_del", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "bool", + "is_add", + { + "default": "true" + } + ], + [ + "vl_api_ip_table_t", + "table" + ], + { + "crc": "0x0ffdaec0" } - }, - { - "ip_punt_redirect": { - "reply": "ip_punt_redirect_reply" + ], + [ + "ip_table_add_del_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + { + "crc": "0xe8d4e804" } - }, - { - "sw_interface_ip6nd_ra_prefix": { - "reply": "sw_interface_ip6nd_ra_prefix_reply" + ], + [ + "ip_table_dump", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + { + "crc": "0x51077d14" } - }, - { - "reset_fib": { - "reply": "reset_fib_reply" + ], + [ + "ip_table_replace_begin", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "vl_api_ip_table_t", + "table" + ], + { + "crc": "0xb9d2e09e" } - }, - { - "ip6_mfib_dump": { - "reply": "ip6_mfib_details", - "stream": true + ], + [ + "ip_table_replace_begin_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + { + "crc": "0xe8d4e804" } - }, - { - "sw_interface_ip6nd_ra_config": { - "reply": "sw_interface_ip6nd_ra_config_reply" + ], + [ + "ip_table_replace_end", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "vl_api_ip_table_t", + "table" + ], + { + "crc": "0xb9d2e09e" } - }, - { - "sw_interface_ip6_enable_disable": { - "reply": "sw_interface_ip6_enable_disable_reply" + ], + [ + "ip_table_replace_end_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + { + "crc": "0xe8d4e804" } - }, - { - "sw_interface_ip6_set_link_local_address": { - "reply": "sw_interface_ip6_set_link_local_address_reply" + ], + [ + "ip_table_flush", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "vl_api_ip_table_t", + "table" + ], + { + "crc": "0xb9d2e09e" } - }, - { - "mfib_signal_dump": { - "reply": "mfib_signal_details", - "stream": true + ], + [ + "ip_table_flush_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + { + "crc": "0xe8d4e804" } - }, - { - "ip_container_proxy_add_del": { - "reply": "ip_container_proxy_add_del_reply" + ], + [ + "ip_table_details", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "vl_api_ip_table_t", + "table" + ], + { + "crc": "0xc79fca0f" } - }, - { - "ip_mfib_dump": { - "reply": "ip_mfib_details", - "stream": true + ], + [ + "ip_route_add_del", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "bool", + "is_add", + { + "default": "true" + } + ], + [ + "bool", + "is_multipath" + ], + [ + "vl_api_ip_route_t", + "route" + ], + { + "crc": "0xc1ff832d" } - }, - { - "ip_address_dump": { - "reply": "ip_address_details", - "stream": true + ], + [ + "ip_route_add_del_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + [ + "u32", + "stats_index" + ], + { + "crc": "0x1992deab" } - }, - { - "ip_dump": { - "reply": "ip_details", - "stream": true + ], + [ + "ip_route_dump", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "vl_api_ip_table_t", + "table" + ], + { + "crc": "0xb9d2e09e" } - }, - { - "ip_neighbor_add_del": { - "reply": "ip_neighbor_add_del_reply" + ], + [ + "ip_route_details", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "vl_api_ip_route_t", + "route" + ], + { + "crc": "0xd1ffaae1" } - }, - { - "proxy_arp_intfc_enable_disable": { - "reply": "proxy_arp_intfc_enable_disable_reply" + ], + [ + "ip_route_lookup", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "u32", + "table_id" + ], + [ + "u8", + "exact" + ], + [ + "vl_api_prefix_t", + "prefix" + ], + { + "crc": "0xe2986185" } - }, - { - "proxy_arp_add_del": { - "reply": "proxy_arp_add_del_reply" + ], + [ + "ip_route_lookup_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + [ + "vl_api_ip_route_t", + "route" + ], + { + "crc": "0xae99de8e" } - }, - { - "ip_add_del_route": { - "reply": "ip_add_del_route_reply" + ], + [ + "set_ip_flow_hash", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "u32", + "vrf_id" + ], + [ + "bool", + "is_ipv6" + ], + [ + "bool", + "src" + ], + [ + "bool", + "dst" + ], + [ + "bool", + "sport" + ], + [ + "bool", + "dport" + ], + [ + "bool", + "proto" + ], + [ + "bool", + "reverse" + ], + [ + "bool", + "symmetric" + ], + { + "crc": "0x084ee09e" } - }, - { - "ip6nd_proxy_dump": { - "reply": "ip6nd_proxy_details", - "stream": true + ], + [ + "set_ip_flow_hash_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + { + "crc": "0xe8d4e804" } - }, - { - "ip_fib_dump": { - "reply": "ip_fib_details", - "stream": true + ], + [ + "set_ip_flow_hash_v2", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "u32", + "table_id" + ], + [ + "vl_api_address_family_t", + "af" + ], + [ + "vl_api_ip_flow_hash_config_t", + "flow_hash_config" + ], + { + "crc": "0x6d132100" } - }, - { - "want_ip4_arp_events": { - "reply": "want_ip4_arp_events_reply" + ], + [ + "set_ip_flow_hash_v2_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + { + "crc": "0xe8d4e804" } - }, - { - "ioam_enable": { - "reply": "ioam_enable_reply" + ], + [ + "set_ip_flow_hash_router_id", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "u32", + "router_id" + ], + { + "crc": "0x03e4f48e" } - }, - { - "ip6_nd_event": { - "reply": null + ], + [ + "set_ip_flow_hash_router_id_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + { + "crc": "0xe8d4e804" } - }, - { - "ip_mroute_add_del": { - "reply": "ip_mroute_add_del_reply" + ], + [ + "sw_interface_ip6_enable_disable", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "vl_api_interface_index_t", + "sw_if_index" + ], + [ + "bool", + "enable" + ], + { + "crc": "0xae6cfcfb" } - }, - { - "ip_source_and_port_range_check_interface_add_del": { - "reply": "ip_source_and_port_range_check_interface_add_del_reply" + ], + [ + "sw_interface_ip6_enable_disable_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + { + "crc": "0xe8d4e804" } - }, - { - "set_ip_flow_hash": { - "reply": "set_ip_flow_hash_reply" + ], + [ + "ip_mtable_dump", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + { + "crc": "0x51077d14" } - } - ], - "vl_api_version": "0xb395c625", - "enums": [], - "messages": [ + ], [ - "ip_table_add_del", + "ip_mtable_details", [ "u16", "_vl_msg_id" @@ -197,29 +1144,49 @@ "u32", "context" ], + [ + "vl_api_ip_table_t", + "table" + ], + { + "crc": "0xb9d2e09e" + } + ], + [ + "ip_mroute_add_del", + [ + "u16", + "_vl_msg_id" + ], [ "u32", - "table_id" + "client_index" ], [ - "u8", - "is_ipv6" + "u32", + "context" ], [ - "u8", - "is_add" + "bool", + "is_add", + { + "default": "true" + } ], [ - "u8", - "name", - 64 + "bool", + "is_multipath" + ], + [ + "vl_api_ip_mroute_t", + "route" ], { - "crc": "0x0240c89d" + "crc": "0x0dd7e790" } ], [ - "ip_table_add_del_reply", + "ip_mroute_add_del_reply", [ "u16", "_vl_msg_id" @@ -232,12 +1199,16 @@ "i32", "retval" ], + [ + "u32", + "stats_index" + ], { - "crc": "0xe8d4e804" + "crc": "0x1992deab" } ], [ - "ip_fib_dump", + "ip_mroute_dump", [ "u16", "_vl_msg_id" @@ -250,12 +1221,34 @@ "u32", "context" ], + [ + "vl_api_ip_table_t", + "table" + ], { - "crc": "0x51077d14" + "crc": "0xb9d2e09e" + } + ], + [ + "ip_mroute_details", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "vl_api_ip_mroute_t", + "route" + ], + { + "crc": "0xc5cb23fc" } ], [ - "ip_fib_details", + "ip_address_details", [ "u16", "_vl_msg_id" @@ -264,40 +1257,137 @@ "u32", "context" ], + [ + "vl_api_interface_index_t", + "sw_if_index" + ], + [ + "vl_api_address_with_prefix_t", + "prefix" + ], + { + "crc": "0xb1199745" + } + ], + [ + "ip_address_dump", + [ + "u16", + "_vl_msg_id" + ], [ "u32", - "table_id" + "client_index" ], [ - "u8", - "table_name", - 64 + "u32", + "context" ], [ - "u8", - "address_length" + "vl_api_interface_index_t", + "sw_if_index" ], [ - "u8", - "address", - 4 + "bool", + "is_ipv6" + ], + { + "crc": "0x2d033de4" + } + ], + [ + "ip_unnumbered_details", + [ + "u16", + "_vl_msg_id" ], [ "u32", - "count" + "context" ], [ - "vl_api_fib_path_t", - "path", - 0, - "count" + "vl_api_interface_index_t", + "sw_if_index" + ], + [ + "vl_api_interface_index_t", + "ip_sw_if_index" + ], + { + "crc": "0xaa12a483" + } + ], + [ + "ip_unnumbered_dump", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "vl_api_interface_index_t", + "sw_if_index", + { + "default": 4294967295 + } ], { - "crc": "0x99dfd73b" + "crc": "0xf9e6675e" } ], [ - "ip6_fib_dump", + "ip_details", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "vl_api_interface_index_t", + "sw_if_index" + ], + [ + "bool", + "is_ipv6" + ], + { + "crc": "0xeb152d07" + } + ], + [ + "ip_dump", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "bool", + "is_ipv6" + ], + { + "crc": "0x98d231ca" + } + ], + [ + "mfib_signal_dump", [ "u16", "_vl_msg_id" @@ -315,7 +1405,7 @@ } ], [ - "ip6_fib_details", + "mfib_signal_details", [ "u16", "_vl_msg_id" @@ -324,40 +1414,131 @@ "u32", "context" ], + [ + "vl_api_interface_index_t", + "sw_if_index" + ], [ "u32", "table_id" ], [ - "u8", - "table_name", - 64 + "vl_api_mprefix_t", + "prefix" ], [ - "u8", - "address_length" + "u16", + "ip_packet_len" ], [ "u8", - "address", - 16 + "ip_packet_data", + 256 + ], + { + "crc": "0x64398a9a" + } + ], + [ + "ip_punt_police", + [ + "u16", + "_vl_msg_id" ], [ "u32", - "count" + "client_index" ], [ - "vl_api_fib_path_t", - "path", - 0, - "count" + "u32", + "context" + ], + [ + "u32", + "policer_index" + ], + [ + "bool", + "is_add", + { + "default": "true" + } + ], + [ + "bool", + "is_ip6" + ], + { + "crc": "0xdb867cea" + } + ], + [ + "ip_punt_police_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" + ], + { + "crc": "0xe8d4e804" + } + ], + [ + "ip_punt_redirect", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "client_index" + ], + [ + "u32", + "context" + ], + [ + "vl_api_punt_redirect_t", + "punt" + ], + [ + "bool", + "is_add", + { + "default": "true" + } + ], + { + "crc": "0xa9a5592c" + } + ], + [ + "ip_punt_redirect_reply", + [ + "u16", + "_vl_msg_id" + ], + [ + "u32", + "context" + ], + [ + "i32", + "retval" ], { - "crc": "0xabd0060e" + "crc": "0xe8d4e804" } ], [ - "ip_neighbor_dump", + "ip_punt_redirect_dump", [ "u16", "_vl_msg_id" @@ -371,19 +1552,19 @@ "context" ], [ - "u32", + "vl_api_interface_index_t", "sw_if_index" ], [ - "u8", + "bool", "is_ipv6" ], { - "crc": "0x6b7bcd0a" + "crc": "0x2d033de4" } ], [ - "ip_neighbor_details", + "ip_punt_redirect_details", [ "u16", "_vl_msg_id" @@ -393,33 +1574,15 @@ "context" ], [ - "u32", - "sw_if_index" - ], - [ - "u8", - "is_static" - ], - [ - "u8", - "is_ipv6" - ], - [ - "u8", - "mac_address", - 6 - ], - [ - "u8", - "ip_address", - 16 + "vl_api_punt_redirect_t", + "punt" ], { - "crc": "0x85e32a72" + "crc": "0x3924f5d3" } ], [ - "ip_neighbor_add_del", + "ip_container_proxy_add_del", [ "u16", "_vl_msg_id" @@ -433,41 +1596,62 @@ "context" ], [ - "u32", + "vl_api_prefix_t", + "pfx" + ], + [ + "vl_api_interface_index_t", "sw_if_index" ], [ - "u8", - "is_add" + "bool", + "is_add", + { + "default": "true" + } + ], + { + "crc": "0x91189f40" + } + ], + [ + "ip_container_proxy_add_del_reply", + [ + "u16", + "_vl_msg_id" ], [ - "u8", - "is_ipv6" + "u32", + "context" ], [ - "u8", - "is_static" + "i32", + "retval" ], + { + "crc": "0xe8d4e804" + } + ], + [ + "ip_container_proxy_dump", [ - "u8", - "is_no_adj_fib" + "u16", + "_vl_msg_id" ], [ - "u8", - "mac_address", - 6 + "u32", + "client_index" ], [ - "u8", - "dst_address", - 16 + "u32", + "context" ], { - "crc": "0x4711eb25" + "crc": "0x51077d14" } ], [ - "ip_neighbor_add_del_reply", + "ip_container_proxy_details", [ "u16", "_vl_msg_id" @@ -477,15 +1661,19 @@ "context" ], [ - "i32", - "retval" + "vl_api_interface_index_t", + "sw_if_index" + ], + [ + "vl_api_prefix_t", + "prefix" ], { - "crc": "0xe8d4e804" + "crc": "0x0ee460e8" } ], [ - "set_ip_flow_hash", + "ip_source_and_port_range_check_add_del", [ "u16", "_vl_msg_id" @@ -499,43 +1687,40 @@ "context" ], [ - "u32", - "vrf_id" - ], - [ - "u8", - "is_ipv6" - ], - [ - "u8", - "src" + "bool", + "is_add", + { + "default": "true" + } ], [ - "u8", - "dst" + "vl_api_prefix_t", + "prefix" ], [ "u8", - "sport" + "number_of_ranges" ], [ - "u8", - "dport" + "u16", + "low_ports", + 32 ], [ - "u8", - "proto" + "u16", + "high_ports", + 32 ], [ - "u8", - "reverse" + "u32", + "vrf_id" ], { - "crc": "0x32ebf737" + "crc": "0x8bfc76f2" } ], [ - "set_ip_flow_hash_reply", + "ip_source_and_port_range_check_add_del_reply", [ "u16", "_vl_msg_id" @@ -553,7 +1738,7 @@ } ], [ - "sw_interface_ip6nd_ra_config", + "ip_source_and_port_range_check_interface_add_del", [ "u16", "_vl_msg_id" @@ -567,67 +1752,38 @@ "context" ], [ - "u32", - "sw_if_index" - ], - [ - "u8", - "suppress" - ], - [ - "u8", - "managed" - ], - [ - "u8", - "other" - ], - [ - "u8", - "ll_option" - ], - [ - "u8", - "send_unicast" - ], - [ - "u8", - "cease" - ], - [ - "u8", - "is_no" - ], - [ - "u8", - "default_router" + "bool", + "is_add", + { + "default": "true" + } ], [ - "u32", - "max_interval" + "vl_api_interface_index_t", + "sw_if_index" ], [ "u32", - "min_interval" + "tcp_in_vrf_id" ], [ "u32", - "lifetime" + "tcp_out_vrf_id" ], [ "u32", - "initial_count" + "udp_in_vrf_id" ], [ "u32", - "initial_interval" + "udp_out_vrf_id" ], { - "crc": "0xc3f02daa" + "crc": "0xe1ba8987" } ], [ - "sw_interface_ip6nd_ra_config_reply", + "ip_source_and_port_range_check_interface_add_del_reply", [ "u16", "_vl_msg_id" @@ -645,7 +1801,7 @@ } ], [ - "sw_interface_ip6nd_ra_prefix", + "sw_interface_ip6_set_link_local_address", [ "u16", "_vl_msg_id" @@ -659,56 +1815,19 @@ "context" ], [ - "u32", + "vl_api_interface_index_t", "sw_if_index" ], [ - "u8", - "address", - 16 - ], - [ - "u8", - "address_length" - ], - [ - "u8", - "use_default" - ], - [ - "u8", - "no_advertise" - ], - [ - "u8", - "off_link" - ], - [ - "u8", - "no_autoconfig" - ], - [ - "u8", - "no_onlink" - ], - [ - "u8", - "is_no" - ], - [ - "u32", - "val_lifetime" - ], - [ - "u32", - "pref_lifetime" + "vl_api_ip6_address_t", + "ip" ], { - "crc": "0xca763c9a" + "crc": "0x2931d9fa" } ], [ - "sw_interface_ip6nd_ra_prefix_reply", + "sw_interface_ip6_set_link_local_address_reply", [ "u16", "_vl_msg_id" @@ -726,7 +1845,7 @@ } ], [ - "ip6nd_proxy_add_del", + "sw_interface_ip6_get_link_local_address", [ "u16", "_vl_msg_id" @@ -740,24 +1859,15 @@ "context" ], [ - "u32", + "vl_api_interface_index_t", "sw_if_index" ], - [ - "u8", - "is_del" - ], - [ - "u8", - "address", - 16 - ], { - "crc": "0xd95f0fa0" + "crc": "0xf9e6675e" } ], [ - "ip6nd_proxy_add_del_reply", + "sw_interface_ip6_get_link_local_address_reply", [ "u16", "_vl_msg_id" @@ -770,12 +1880,16 @@ "i32", "retval" ], + [ + "vl_api_ip6_address_t", + "ip" + ], { - "crc": "0xe8d4e804" + "crc": "0xd16b7130" } ], [ - "ip6nd_proxy_details", + "ioam_enable", [ "u16", "_vl_msg_id" @@ -789,38 +1903,53 @@ "context" ], [ - "u32", - "sw_if_index" + "u16", + "id" ], [ - "u8", - "address", - 16 + "bool", + "seqno" + ], + [ + "bool", + "analyse" + ], + [ + "bool", + "pot_enable" + ], + [ + "bool", + "trace_enable" + ], + [ + "u32", + "node_id" ], { - "crc": "0xd73bf1ab" + "crc": "0x51ccd868" } ], [ - "ip6nd_proxy_dump", + "ioam_enable_reply", [ "u16", "_vl_msg_id" ], [ "u32", - "client_index" + "context" ], [ - "u32", - "context" + "i32", + "retval" ], { - "crc": "0x51077d14" + "crc": "0xe8d4e804" } ], [ - "sw_interface_ip6_enable_disable", + "ioam_disable", [ "u16", "_vl_msg_id" @@ -834,19 +1963,15 @@ "context" ], [ - "u32", - "sw_if_index" - ], - [ - "u8", - "enable" + "u16", + "id" ], { - "crc": "0xa36fadc0" + "crc": "0x6b16a45e" } ], [ - "sw_interface_ip6_enable_disable_reply", + "ioam_disable_reply", [ "u16", "_vl_msg_id" @@ -864,7 +1989,7 @@ } ], [ - "sw_interface_ip6_set_link_local_address", + "ip_reassembly_set", [ "u16", "_vl_msg_id" @@ -879,19 +2004,34 @@ ], [ "u32", - "sw_if_index" + "timeout_ms" ], [ - "u8", - "address", - 16 + "u32", + "max_reassemblies" + ], + [ + "u32", + "max_reassembly_length" + ], + [ + "u32", + "expire_walk_interval_ms" + ], + [ + "bool", + "is_ip6" + ], + [ + "vl_api_ip_reass_type_t", + "type" ], { - "crc": "0xd73bf1ab" + "crc": "0x16467d25" } ], [ - "sw_interface_ip6_set_link_local_address_reply", + "ip_reassembly_set_reply", [ "u16", "_vl_msg_id" @@ -909,7 +2049,7 @@ } ], [ - "ip_add_del_route", + "ip_reassembly_get", [ "u16", "_vl_msg_id" @@ -922,1325 +2062,1517 @@ "u32", "context" ], + [ + "bool", + "is_ip6" + ], + [ + "vl_api_ip_reass_type_t", + "type" + ], + { + "crc": "0xea13ff63" + } + ], + [ + "ip_reassembly_get_reply", + [ + "u16", + "_vl_msg_id" + ], [ "u32", - "next_hop_sw_if_index" + "context" + ], + [ + "i32", + "retval" ], [ "u32", - "table_id" + "timeout_ms" ], [ "u32", - "classify_table_index" + "max_reassemblies" ], [ "u32", - "next_hop_table_id" + "max_reassembly_length" ], [ "u32", - "next_hop_id" + "expire_walk_interval_ms" ], [ - "u8", - "is_add" + "bool", + "is_ip6" ], + { + "crc": "0xd5eb8d34" + } + ], + [ + "ip_reassembly_enable_disable", [ - "u8", - "is_drop" + "u16", + "_vl_msg_id" ], [ - "u8", - "is_unreach" + "u32", + "client_index" ], [ - "u8", - "is_prohibit" + "u32", + "context" ], [ - "u8", - "is_ipv6" + "vl_api_interface_index_t", + "sw_if_index" ], [ - "u8", - "is_local" + "bool", + "enable_ip4" ], [ - "u8", - "is_classify" + "bool", + "enable_ip6" ], [ - "u8", - "is_multipath" + "vl_api_ip_reass_type_t", + "type" ], + { + "crc": "0x885c85a6" + } + ], + [ + "ip_reassembly_enable_disable_reply", [ - "u8", - "is_resolve_host" + "u16", + "_vl_msg_id" ], [ - "u8", - "is_resolve_attached" + "u32", + "context" ], [ - "u8", - "is_dvr" + "i32", + "retval" ], + { + "crc": "0xe8d4e804" + } + ] + ], + "unions": [ + [ + "address_union", [ - "u8", - "is_source_lookup" + "vl_api_ip4_address_t", + "ip4" ], [ - "u8", - "is_udp_encap" - ], + "vl_api_ip6_address_t", + "ip6" + ] + ], + [ + "address_union", [ - "u8", - "next_hop_weight" + "vl_api_ip4_address_t", + "ip4" ], [ - "u8", - "next_hop_preference" - ], + "vl_api_ip6_address_t", + "ip6" + ] + ], + [ + "address_union", [ - "u8", - "next_hop_proto" + "vl_api_ip4_address_t", + "ip4" ], [ - "u8", - "dst_address_length" + "vl_api_ip6_address_t", + "ip6" + ] + ] + ], + "enums": [ + [ + "if_status_flags", + [ + "IF_STATUS_API_FLAG_ADMIN_UP", + 1 ], [ - "u8", - "dst_address", - 16 + "IF_STATUS_API_FLAG_LINK_UP", + 2 ], + { + "enumtype": "u32" + } + ], + [ + "mtu_proto", [ - "u8", - "next_hop_address", - 16 + "MTU_PROTO_API_L3", + 0 ], [ - "u8", - "next_hop_n_out_labels" + "MTU_PROTO_API_IP4", + 1 ], [ - "u32", - "next_hop_via_label" + "MTU_PROTO_API_IP6", + 2 ], [ - "u32", - "next_hop_out_label_stack", - 0, - "next_hop_n_out_labels" + "MTU_PROTO_API_MPLS", + 3 ], { - "crc": "0xc85f8290" + "enumtype": "u32" } ], [ - "ip_add_del_route_reply", + "link_duplex", [ - "u16", - "_vl_msg_id" + "LINK_DUPLEX_API_UNKNOWN", + 0 ], [ - "u32", - "context" + "LINK_DUPLEX_API_HALF", + 1 ], [ - "i32", - "retval" + "LINK_DUPLEX_API_FULL", + 2 ], { - "crc": "0xe8d4e804" + "enumtype": "u32" } ], [ - "ip_mroute_add_del", - [ - "u16", - "_vl_msg_id" - ], + "sub_if_flags", [ - "u32", - "client_index" + "SUB_IF_API_FLAG_NO_TAGS", + 1 ], [ - "u32", - "context" + "SUB_IF_API_FLAG_ONE_TAG", + 2 ], [ - "u32", - "next_hop_sw_if_index" + "SUB_IF_API_FLAG_TWO_TAGS", + 4 ], [ - "u32", - "table_id" + "SUB_IF_API_FLAG_DOT1AD", + 8 ], [ - "u32", - "entry_flags" + "SUB_IF_API_FLAG_EXACT_MATCH", + 16 ], [ - "u32", - "itf_flags" + "SUB_IF_API_FLAG_DEFAULT", + 32 ], [ - "u32", - "rpf_id" + "SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY", + 64 ], [ - "u32", - "bier_imp" + "SUB_IF_API_FLAG_INNER_VLAN_ID_ANY", + 128 ], [ - "u16", - "grp_address_length" + "SUB_IF_API_FLAG_MASK_VNET", + 254 ], [ - "u8", - "next_hop_afi" + "SUB_IF_API_FLAG_DOT1AH", + 256 ], + { + "enumtype": "u32" + } + ], + [ + "rx_mode", [ - "u8", - "is_add" + "RX_MODE_API_UNKNOWN", + 0 ], [ - "u8", - "is_ipv6" + "RX_MODE_API_POLLING", + 1 ], [ - "u8", - "is_local" + "RX_MODE_API_INTERRUPT", + 2 ], [ - "u8", - "grp_address", - 16 + "RX_MODE_API_ADAPTIVE", + 3 ], [ - "u8", - "src_address", - 16 + "RX_MODE_API_DEFAULT", + 4 ], { - "crc": "0xc37112f7" + "enumtype": "u32" } ], [ - "ip_mroute_add_del_reply", + "if_type", [ - "u16", - "_vl_msg_id" + "IF_API_TYPE_HARDWARE", + 0 ], [ - "u32", - "context" + "IF_API_TYPE_SUB", + 1 ], [ - "i32", - "retval" + "IF_API_TYPE_P2P", + 2 + ], + [ + "IF_API_TYPE_PIPE", + 3 ], { - "crc": "0xe8d4e804" + "enumtype": "u32" } ], [ - "ip_mfib_dump", - [ - "u16", - "_vl_msg_id" - ], + "address_family", [ - "u32", - "client_index" + "ADDRESS_IP4", + 0 ], [ - "u32", - "context" + "ADDRESS_IP6", + 1 ], { - "crc": "0x51077d14" + "enumtype": "u8" } ], [ - "ip_mfib_details", - [ - "u16", - "_vl_msg_id" - ], + "ip_feature_location", [ - "u32", - "context" + "IP_API_FEATURE_INPUT", + 0 ], [ - "u32", - "table_id" + "IP_API_FEATURE_OUTPUT", + 1 ], [ - "u32", - "entry_flags" + "IP_API_FEATURE_LOCAL", + 2 ], [ - "u32", - "rpf_id" + "IP_API_FEATURE_PUNT", + 3 ], [ - "u8", - "address_length" + "IP_API_FEATURE_DROP", + 4 ], + { + "enumtype": "u8" + } + ], + [ + "ip_ecn", [ - "u8", - "grp_address", - 4 + "IP_API_ECN_NONE", + 0 ], [ - "u8", - "src_address", - 4 + "IP_API_ECN_ECT0", + 1 ], [ - "u32", - "count" + "IP_API_ECN_ECT1", + 2 ], [ - "vl_api_fib_path_t", - "path", - 0, - "count" + "IP_API_ECN_CE", + 3 ], { - "crc": "0x5e530d5e" + "enumtype": "u8" } ], [ - "ip6_mfib_dump", + "ip_dscp", [ - "u16", - "_vl_msg_id" + "IP_API_DSCP_CS0", + 0 ], [ - "u32", - "client_index" + "IP_API_DSCP_CS1", + 8 ], [ - "u32", - "context" + "IP_API_DSCP_AF11", + 10 ], - { - "crc": "0x51077d14" - } - ], - [ - "ip6_mfib_details", [ - "u16", - "_vl_msg_id" + "IP_API_DSCP_AF12", + 12 ], [ - "u32", - "context" + "IP_API_DSCP_AF13", + 14 ], [ - "u32", - "table_id" + "IP_API_DSCP_CS2", + 16 ], [ - "u8", - "address_length" + "IP_API_DSCP_AF21", + 18 ], [ - "u8", - "grp_address", - 16 + "IP_API_DSCP_AF22", + 20 ], [ - "u8", - "src_address", - 16 + "IP_API_DSCP_AF23", + 22 ], [ - "u32", - "count" + "IP_API_DSCP_CS3", + 24 ], [ - "vl_api_fib_path_t", - "path", - 0, - "count" + "IP_API_DSCP_AF31", + 26 ], - { - "crc": "0xe02dcb4b" - } - ], - [ - "ip_address_details", [ - "u16", - "_vl_msg_id" + "IP_API_DSCP_AF32", + 28 ], [ - "u32", - "client_index" + "IP_API_DSCP_AF33", + 30 ], [ - "u32", - "context" + "IP_API_DSCP_CS4", + 32 ], [ - "u8", - "ip", - 16 + "IP_API_DSCP_AF41", + 34 ], [ - "u8", - "prefix_length" + "IP_API_DSCP_AF42", + 36 ], [ - "u32", - "sw_if_index" + "IP_API_DSCP_AF43", + 38 ], [ - "u8", - "is_ipv6" + "IP_API_DSCP_CS5", + 40 + ], + [ + "IP_API_DSCP_EF", + 46 + ], + [ + "IP_API_DSCP_CS6", + 48 + ], + [ + "IP_API_DSCP_CS7", + 50 ], { - "crc": "0xbc7442f2" + "enumtype": "u8" } ], [ - "ip_address_dump", + "ip_proto", [ - "u16", - "_vl_msg_id" + "IP_API_PROTO_HOPOPT", + 0 ], [ - "u32", - "client_index" + "IP_API_PROTO_ICMP", + 1 ], [ - "u32", - "context" + "IP_API_PROTO_IGMP", + 2 ], [ - "u32", - "sw_if_index" + "IP_API_PROTO_TCP", + 6 ], [ - "u8", - "is_ipv6" + "IP_API_PROTO_UDP", + 17 ], - { - "crc": "0x6b7bcd0a" - } - ], - [ - "ip_details", [ - "u16", - "_vl_msg_id" + "IP_API_PROTO_GRE", + 47 ], [ - "u32", - "sw_if_index" + "IP_API_PROTO_ESP", + 50 ], [ - "u32", - "context" + "IP_API_PROTO_AH", + 51 ], [ - "u8", - "is_ipv6" + "IP_API_PROTO_ICMP6", + 58 + ], + [ + "IP_API_PROTO_EIGRP", + 88 + ], + [ + "IP_API_PROTO_OSPF", + 89 + ], + [ + "IP_API_PROTO_SCTP", + 132 + ], + [ + "IP_API_PROTO_RESERVED", + 255 ], { - "crc": "0x452ffc5a" + "enumtype": "u8" } ], [ - "ip_dump", + "fib_path_nh_proto", [ - "u16", - "_vl_msg_id" + "FIB_API_PATH_NH_PROTO_IP4", + 0 ], [ - "u32", - "client_index" + "FIB_API_PATH_NH_PROTO_IP6", + 1 ], [ - "u32", - "context" + "FIB_API_PATH_NH_PROTO_MPLS", + 2 ], [ - "u8", - "is_ipv6" + "FIB_API_PATH_NH_PROTO_ETHERNET", + 3 + ], + [ + "FIB_API_PATH_NH_PROTO_BIER", + 4 ], { - "crc": "0xde883da4" + "enumtype": "u32" } ], [ - "mfib_signal_dump", + "fib_path_flags", [ - "u16", - "_vl_msg_id" + "FIB_API_PATH_FLAG_NONE", + 0 ], [ - "u32", - "client_index" + "FIB_API_PATH_FLAG_RESOLVE_VIA_ATTACHED", + 1 ], [ - "u32", - "context" + "FIB_API_PATH_FLAG_RESOLVE_VIA_HOST", + 2 + ], + [ + "FIB_API_PATH_FLAG_POP_PW_CW", + 4 ], { - "crc": "0x51077d14" + "enumtype": "u32" } ], [ - "mfib_signal_details", + "fib_path_type", [ - "u16", - "_vl_msg_id" + "FIB_API_PATH_TYPE_NORMAL", + 0 ], [ - "u32", - "client_index" + "FIB_API_PATH_TYPE_LOCAL", + 1 ], [ - "u32", - "context" + "FIB_API_PATH_TYPE_DROP", + 2 ], [ - "u32", - "sw_if_index" + "FIB_API_PATH_TYPE_UDP_ENCAP", + 3 ], [ - "u32", - "table_id" + "FIB_API_PATH_TYPE_BIER_IMP", + 4 ], [ - "u16", - "grp_address_len" + "FIB_API_PATH_TYPE_ICMP_UNREACH", + 5 ], [ - "u8", - "grp_address", - 16 + "FIB_API_PATH_TYPE_ICMP_PROHIBIT", + 6 ], [ - "u8", - "src_address", - 16 + "FIB_API_PATH_TYPE_SOURCE_LOOKUP", + 7 ], [ - "u16", - "ip_packet_len" + "FIB_API_PATH_TYPE_DVR", + 8 ], [ - "u8", - "ip_packet_data", - 256 + "FIB_API_PATH_TYPE_INTERFACE_RX", + 9 + ], + [ + "FIB_API_PATH_TYPE_CLASSIFY", + 10 ], { - "crc": "0x791bbeab" + "enumtype": "u32" } ], [ - "ip_punt_police", + "address_family", [ - "u16", - "_vl_msg_id" + "ADDRESS_IP4", + 0 ], [ - "u32", - "client_index" + "ADDRESS_IP6", + 1 ], + { + "enumtype": "u8" + } + ], + [ + "ip_feature_location", [ - "u32", - "context" + "IP_API_FEATURE_INPUT", + 0 ], [ - "u32", - "policer_index" + "IP_API_FEATURE_OUTPUT", + 1 ], [ - "u8", - "is_add" + "IP_API_FEATURE_LOCAL", + 2 ], [ - "u8", - "is_ip6" + "IP_API_FEATURE_PUNT", + 3 + ], + [ + "IP_API_FEATURE_DROP", + 4 ], { - "crc": "0x38691592" + "enumtype": "u8" } ], [ - "ip_punt_police_reply", + "ip_ecn", [ - "u16", - "_vl_msg_id" + "IP_API_ECN_NONE", + 0 ], [ - "u32", - "context" + "IP_API_ECN_ECT0", + 1 ], [ - "i32", - "retval" + "IP_API_ECN_ECT1", + 2 + ], + [ + "IP_API_ECN_CE", + 3 ], { - "crc": "0xe8d4e804" + "enumtype": "u8" } ], [ - "ip_punt_redirect", + "ip_dscp", [ - "u16", - "_vl_msg_id" + "IP_API_DSCP_CS0", + 0 ], [ - "u32", - "client_index" + "IP_API_DSCP_CS1", + 8 ], [ - "u32", - "context" + "IP_API_DSCP_AF11", + 10 ], [ - "u32", - "rx_sw_if_index" + "IP_API_DSCP_AF12", + 12 ], [ - "u32", - "tx_sw_if_index" + "IP_API_DSCP_AF13", + 14 ], [ - "u8", - "is_add" + "IP_API_DSCP_CS2", + 16 ], [ - "u8", - "is_ip6" + "IP_API_DSCP_AF21", + 18 ], [ - "u8", - "nh", - 16 + "IP_API_DSCP_AF22", + 20 ], - { - "crc": "0x996b6603" - } - ], - [ - "ip_punt_redirect_reply", [ - "u16", - "_vl_msg_id" + "IP_API_DSCP_AF23", + 22 ], [ - "u32", - "context" + "IP_API_DSCP_CS3", + 24 ], [ - "i32", - "retval" + "IP_API_DSCP_AF31", + 26 ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip_container_proxy_add_del", [ - "u16", - "_vl_msg_id" + "IP_API_DSCP_AF32", + 28 ], [ - "u32", - "client_index" + "IP_API_DSCP_AF33", + 30 ], [ - "u32", - "context" + "IP_API_DSCP_CS4", + 32 ], [ - "u8", - "ip", - 16 + "IP_API_DSCP_AF41", + 34 ], [ - "u8", - "is_ip4" + "IP_API_DSCP_AF42", + 36 ], [ - "u8", - "plen" + "IP_API_DSCP_AF43", + 38 ], [ - "u32", - "sw_if_index" + "IP_API_DSCP_CS5", + 40 ], [ - "u8", - "is_add" + "IP_API_DSCP_EF", + 46 + ], + [ + "IP_API_DSCP_CS6", + 48 + ], + [ + "IP_API_DSCP_CS7", + 50 ], { - "crc": "0x0a355d39" + "enumtype": "u8" } ], [ - "ip_container_proxy_add_del_reply", + "ip_proto", [ - "u16", - "_vl_msg_id" + "IP_API_PROTO_HOPOPT", + 0 ], [ - "u32", - "context" + "IP_API_PROTO_ICMP", + 1 ], [ - "i32", - "retval" + "IP_API_PROTO_IGMP", + 2 ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip_source_and_port_range_check_add_del", [ - "u16", - "_vl_msg_id" + "IP_API_PROTO_TCP", + 6 ], [ - "u32", - "client_index" + "IP_API_PROTO_UDP", + 17 ], [ - "u32", - "context" + "IP_API_PROTO_GRE", + 47 ], [ - "u8", - "is_ipv6" + "IP_API_PROTO_ESP", + 50 ], [ - "u8", - "is_add" + "IP_API_PROTO_AH", + 51 ], [ - "u8", - "mask_length" + "IP_API_PROTO_ICMP6", + 58 ], [ - "u8", - "address", - 16 + "IP_API_PROTO_EIGRP", + 88 ], [ - "u8", - "number_of_ranges" + "IP_API_PROTO_OSPF", + 89 ], [ - "u16", - "low_ports", - 32 + "IP_API_PROTO_SCTP", + 132 ], [ - "u16", - "high_ports", - 32 + "IP_API_PROTO_RESERVED", + 255 + ], + { + "enumtype": "u8" + } + ], + [ + "fib_path_nh_proto", + [ + "FIB_API_PATH_NH_PROTO_IP4", + 0 ], [ - "u32", - "vrf_id" + "FIB_API_PATH_NH_PROTO_IP6", + 1 + ], + [ + "FIB_API_PATH_NH_PROTO_MPLS", + 2 + ], + [ + "FIB_API_PATH_NH_PROTO_ETHERNET", + 3 + ], + [ + "FIB_API_PATH_NH_PROTO_BIER", + 4 ], { - "crc": "0x03d6b03a" + "enumtype": "u32" } ], [ - "ip_source_and_port_range_check_add_del_reply", + "fib_path_flags", [ - "u16", - "_vl_msg_id" + "FIB_API_PATH_FLAG_NONE", + 0 ], [ - "u32", - "context" + "FIB_API_PATH_FLAG_RESOLVE_VIA_ATTACHED", + 1 ], [ - "i32", - "retval" + "FIB_API_PATH_FLAG_RESOLVE_VIA_HOST", + 2 + ], + [ + "FIB_API_PATH_FLAG_POP_PW_CW", + 4 ], { - "crc": "0xe8d4e804" + "enumtype": "u32" } ], [ - "ip_source_and_port_range_check_interface_add_del", + "fib_path_type", [ - "u16", - "_vl_msg_id" + "FIB_API_PATH_TYPE_NORMAL", + 0 ], [ - "u32", - "client_index" + "FIB_API_PATH_TYPE_LOCAL", + 1 ], [ - "u32", - "context" + "FIB_API_PATH_TYPE_DROP", + 2 ], [ - "u8", - "is_add" + "FIB_API_PATH_TYPE_UDP_ENCAP", + 3 ], [ - "u32", - "sw_if_index" + "FIB_API_PATH_TYPE_BIER_IMP", + 4 ], [ - "u32", - "tcp_in_vrf_id" + "FIB_API_PATH_TYPE_ICMP_UNREACH", + 5 ], [ - "u32", - "tcp_out_vrf_id" + "FIB_API_PATH_TYPE_ICMP_PROHIBIT", + 6 ], [ - "u32", - "udp_in_vrf_id" + "FIB_API_PATH_TYPE_SOURCE_LOOKUP", + 7 ], [ - "u32", - "udp_out_vrf_id" + "FIB_API_PATH_TYPE_DVR", + 8 + ], + [ + "FIB_API_PATH_TYPE_INTERFACE_RX", + 9 + ], + [ + "FIB_API_PATH_TYPE_CLASSIFY", + 10 ], { - "crc": "0x6966bc44" + "enumtype": "u32" } ], [ - "ip_source_and_port_range_check_interface_add_del_reply", - [ - "u16", - "_vl_msg_id" - ], + "address_family", [ - "u32", - "context" + "ADDRESS_IP4", + 0 ], [ - "i32", - "retval" + "ADDRESS_IP6", + 1 ], { - "crc": "0xe8d4e804" + "enumtype": "u8" } ], [ - "want_ip4_arp_events", - [ - "u16", - "_vl_msg_id" - ], + "ip_feature_location", [ - "u32", - "client_index" + "IP_API_FEATURE_INPUT", + 0 ], [ - "u32", - "context" + "IP_API_FEATURE_OUTPUT", + 1 ], [ - "u8", - "enable_disable" + "IP_API_FEATURE_LOCAL", + 2 ], [ - "u32", - "pid" + "IP_API_FEATURE_PUNT", + 3 ], [ - "u32", - "address" + "IP_API_FEATURE_DROP", + 4 ], { - "crc": "0x77e06379" + "enumtype": "u8" } ], [ - "want_ip4_arp_events_reply", + "ip_ecn", [ - "u16", - "_vl_msg_id" + "IP_API_ECN_NONE", + 0 ], [ - "u32", - "context" + "IP_API_ECN_ECT0", + 1 ], [ - "i32", - "retval" + "IP_API_ECN_ECT1", + 2 + ], + [ + "IP_API_ECN_CE", + 3 ], { - "crc": "0xe8d4e804" + "enumtype": "u8" } ], [ - "ip4_arp_event", - [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "client_index" - ], + "ip_dscp", [ - "u32", - "address" + "IP_API_DSCP_CS0", + 0 ], [ - "u32", - "pid" + "IP_API_DSCP_CS1", + 8 ], [ - "u32", - "sw_if_index" + "IP_API_DSCP_AF11", + 10 ], [ - "u8", - "new_mac", - 6 + "IP_API_DSCP_AF12", + 12 ], [ - "u8", - "mac_ip" + "IP_API_DSCP_AF13", + 14 ], - { - "crc": "0xef7235f7" - } - ], - [ - "want_ip6_nd_events", [ - "u16", - "_vl_msg_id" + "IP_API_DSCP_CS2", + 16 ], [ - "u32", - "client_index" + "IP_API_DSCP_AF21", + 18 ], [ - "u32", - "context" + "IP_API_DSCP_AF22", + 20 ], [ - "u8", - "enable_disable" + "IP_API_DSCP_AF23", + 22 ], [ - "u32", - "pid" + "IP_API_DSCP_CS3", + 24 ], [ - "u8", - "address", - 16 + "IP_API_DSCP_AF31", + 26 ], - { - "crc": "0x1cf65fbb" - } - ], - [ - "want_ip6_nd_events_reply", [ - "u16", - "_vl_msg_id" + "IP_API_DSCP_AF32", + 28 ], [ - "u32", - "context" + "IP_API_DSCP_AF33", + 30 ], [ - "i32", - "retval" + "IP_API_DSCP_CS4", + 32 ], - { - "crc": "0xe8d4e804" - } - ], - [ - "ip6_nd_event", [ - "u16", - "_vl_msg_id" + "IP_API_DSCP_AF41", + 34 ], [ - "u32", - "client_index" + "IP_API_DSCP_AF42", + 36 ], [ - "u32", - "pid" + "IP_API_DSCP_AF43", + 38 ], [ - "u32", - "sw_if_index" + "IP_API_DSCP_CS5", + 40 ], [ - "u8", - "address", - 16 + "IP_API_DSCP_EF", + 46 ], [ - "u8", - "new_mac", - 6 + "IP_API_DSCP_CS6", + 48 ], [ - "u8", - "mac_ip" + "IP_API_DSCP_CS7", + 50 ], { - "crc": "0x96ab2fdd" + "enumtype": "u8" } ], [ - "proxy_arp_add_del", - [ - "u16", - "_vl_msg_id" - ], + "ip_proto", [ - "u32", - "client_index" - ], - [ - "u32", - "context" + "IP_API_PROTO_HOPOPT", + 0 ], [ - "u32", - "vrf_id" + "IP_API_PROTO_ICMP", + 1 ], [ - "u8", - "is_add" + "IP_API_PROTO_IGMP", + 2 ], [ - "u8", - "low_address", - 4 + "IP_API_PROTO_TCP", + 6 ], [ - "u8", - "hi_address", - 4 + "IP_API_PROTO_UDP", + 17 ], - { - "crc": "0xc2442918" - } - ], - [ - "proxy_arp_add_del_reply", [ - "u16", - "_vl_msg_id" + "IP_API_PROTO_GRE", + 47 ], [ - "u32", - "context" + "IP_API_PROTO_ESP", + 50 ], [ - "i32", - "retval" + "IP_API_PROTO_AH", + 51 ], - { - "crc": "0xe8d4e804" - } - ], - [ - "proxy_arp_intfc_enable_disable", [ - "u16", - "_vl_msg_id" + "IP_API_PROTO_ICMP6", + 58 ], [ - "u32", - "client_index" + "IP_API_PROTO_EIGRP", + 88 ], [ - "u32", - "context" + "IP_API_PROTO_OSPF", + 89 ], [ - "u32", - "sw_if_index" + "IP_API_PROTO_SCTP", + 132 ], [ - "u8", - "enable_disable" + "IP_API_PROTO_RESERVED", + 255 ], { - "crc": "0x69d24598" + "enumtype": "u8" } ], [ - "proxy_arp_intfc_enable_disable_reply", + "mfib_entry_flags", [ - "u16", - "_vl_msg_id" + "MFIB_API_ENTRY_FLAG_NONE", + 0 ], [ - "u32", - "context" + "MFIB_API_ENTRY_FLAG_SIGNAL", + 1 ], [ - "i32", - "retval" + "MFIB_API_ENTRY_FLAG_DROP", + 2 + ], + [ + "MFIB_API_ENTRY_FLAG_CONNECTED", + 4 + ], + [ + "MFIB_API_ENTRY_FLAG_ACCEPT_ALL_ITF", + 8 ], { - "crc": "0xe8d4e804" + "enumtype": "u32" } ], [ - "reset_fib", + "mfib_itf_flags", [ - "u16", - "_vl_msg_id" + "MFIB_API_ITF_FLAG_NONE", + 0 ], [ - "u32", - "client_index" + "MFIB_API_ITF_FLAG_NEGATE_SIGNAL", + 1 ], [ - "u32", - "context" + "MFIB_API_ITF_FLAG_ACCEPT", + 2 ], [ - "u32", - "vrf_id" + "MFIB_API_ITF_FLAG_FORWARD", + 4 ], [ - "u8", - "is_ipv6" + "MFIB_API_ITF_FLAG_SIGNAL_PRESENT", + 8 + ], + [ + "MFIB_API_ITF_FLAG_DONT_PRESERVE", + 16 ], { - "crc": "0x8553ebd9" + "enumtype": "u32" } ], [ - "reset_fib_reply", + "if_status_flags", [ - "u16", - "_vl_msg_id" - ], - [ - "u32", - "context" + "IF_STATUS_API_FLAG_ADMIN_UP", + 1 ], [ - "i32", - "retval" + "IF_STATUS_API_FLAG_LINK_UP", + 2 ], { - "crc": "0xe8d4e804" + "enumtype": "u32" } ], [ - "set_arp_neighbor_limit", - [ - "u16", - "_vl_msg_id" - ], + "mtu_proto", [ - "u32", - "client_index" + "MTU_PROTO_API_L3", + 0 ], [ - "u32", - "context" + "MTU_PROTO_API_IP4", + 1 ], [ - "u8", - "is_ipv6" + "MTU_PROTO_API_IP6", + 2 ], [ - "u32", - "arp_neighbor_limit" + "MTU_PROTO_API_MPLS", + 3 ], { - "crc": "0x97d01fd6" + "enumtype": "u32" } ], [ - "set_arp_neighbor_limit_reply", + "link_duplex", [ - "u16", - "_vl_msg_id" + "LINK_DUPLEX_API_UNKNOWN", + 0 ], [ - "u32", - "context" + "LINK_DUPLEX_API_HALF", + 1 ], [ - "i32", - "retval" + "LINK_DUPLEX_API_FULL", + 2 ], { - "crc": "0xe8d4e804" + "enumtype": "u32" } ], [ - "ioam_enable", + "sub_if_flags", [ - "u16", - "_vl_msg_id" + "SUB_IF_API_FLAG_NO_TAGS", + 1 ], [ - "u32", - "client_index" + "SUB_IF_API_FLAG_ONE_TAG", + 2 ], [ - "u32", - "context" + "SUB_IF_API_FLAG_TWO_TAGS", + 4 ], [ - "u16", - "id" + "SUB_IF_API_FLAG_DOT1AD", + 8 ], [ - "u8", - "seqno" + "SUB_IF_API_FLAG_EXACT_MATCH", + 16 ], [ - "u8", - "analyse" + "SUB_IF_API_FLAG_DEFAULT", + 32 ], [ - "u8", - "pot_enable" + "SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY", + 64 ], [ - "u8", - "trace_enable" + "SUB_IF_API_FLAG_INNER_VLAN_ID_ANY", + 128 ], [ - "u32", - "node_id" + "SUB_IF_API_FLAG_MASK_VNET", + 254 + ], + [ + "SUB_IF_API_FLAG_DOT1AH", + 256 ], { - "crc": "0x9392e032" + "enumtype": "u32" } ], [ - "ioam_enable_reply", + "rx_mode", [ - "u16", - "_vl_msg_id" + "RX_MODE_API_UNKNOWN", + 0 ], [ - "u32", - "context" + "RX_MODE_API_POLLING", + 1 ], [ - "i32", - "retval" + "RX_MODE_API_INTERRUPT", + 2 + ], + [ + "RX_MODE_API_ADAPTIVE", + 3 + ], + [ + "RX_MODE_API_DEFAULT", + 4 ], { - "crc": "0xe8d4e804" + "enumtype": "u32" } ], [ - "ioam_disable", + "if_type", [ - "u16", - "_vl_msg_id" + "IF_API_TYPE_HARDWARE", + 0 ], [ - "u32", - "client_index" + "IF_API_TYPE_SUB", + 1 ], [ - "u32", - "context" + "IF_API_TYPE_P2P", + 2 ], [ - "u16", - "id" + "IF_API_TYPE_PIPE", + 3 ], { - "crc": "0x6b16a45e" + "enumtype": "u32" } ], [ - "ioam_disable_reply", - [ - "u16", - "_vl_msg_id" - ], + "ip_reass_type", [ - "u32", - "context" + "IP_REASS_TYPE_FULL", + 0 ], [ - "i32", - "retval" + "IP_REASS_TYPE_SHALLOW_VIRTUAL", + 1 ], { - "crc": "0xe8d4e804" + "enumtype": "u32" } ] ], - "types": [ + "enumflags": [ [ - "fib_path", + "ip_flow_hash_config", [ - "u32", - "sw_if_index" - ], - [ - "u32", - "table_id" - ], - [ - "u8", - "weight" + "IP_API_FLOW_HASH_SRC_IP", + 1 ], [ - "u8", - "preference" + "IP_API_FLOW_HASH_DST_IP", + 2 ], [ - "u8", - "is_local" + "IP_API_FLOW_HASH_SRC_PORT", + 4 ], [ - "u8", - "is_drop" + "IP_API_FLOW_HASH_DST_PORT", + 8 ], [ - "u8", - "is_unreach" + "IP_API_FLOW_HASH_PROTO", + 16 ], [ - "u8", - "is_prohibit" + "IP_API_FLOW_HASH_REVERSE", + 32 ], [ - "u8", - "afi" + "IP_API_FLOW_HASH_SYMETRIC", + 64 ], [ - "u8", - "next_hop", - 16 + "IP_API_FLOW_HASH_FLOW_LABEL", + 128 ], { - "crc": "0xcd899e0a" + "enumtype": "u32" } ] - ] + ], + "services": { + "ip_table_add_del": { + "reply": "ip_table_add_del_reply" + }, + "ip_table_dump": { + "reply": "ip_table_details", + "stream": true + }, + "ip_table_replace_begin": { + "reply": "ip_table_replace_begin_reply" + }, + "ip_table_replace_end": { + "reply": "ip_table_replace_end_reply" + }, + "ip_table_flush": { + "reply": "ip_table_flush_reply" + }, + "ip_route_add_del": { + "reply": "ip_route_add_del_reply" + }, + "ip_route_dump": { + "reply": "ip_route_details", + "stream": true + }, + "ip_route_lookup": { + "reply": "ip_route_lookup_reply" + }, + "set_ip_flow_hash": { + "reply": "set_ip_flow_hash_reply" + }, + "set_ip_flow_hash_v2": { + "reply": "set_ip_flow_hash_v2_reply" + }, + "set_ip_flow_hash_router_id": { + "reply": "set_ip_flow_hash_router_id_reply" + }, + "sw_interface_ip6_enable_disable": { + "reply": "sw_interface_ip6_enable_disable_reply" + }, + "ip_mtable_dump": { + "reply": "ip_mtable_details", + "stream": true + }, + "ip_mroute_add_del": { + "reply": "ip_mroute_add_del_reply" + }, + "ip_mroute_dump": { + "reply": "ip_mroute_details", + "stream": true + }, + "ip_address_dump": { + "reply": "ip_address_details", + "stream": true + }, + "ip_unnumbered_dump": { + "reply": "ip_unnumbered_details", + "stream": true + }, + "ip_dump": { + "reply": "ip_details", + "stream": true + }, + "mfib_signal_dump": { + "reply": "mfib_signal_details", + "stream": true + }, + "ip_punt_police": { + "reply": "ip_punt_police_reply" + }, + "ip_punt_redirect": { + "reply": "ip_punt_redirect_reply" + }, + "ip_punt_redirect_dump": { + "reply": "ip_punt_redirect_details", + "stream": true + }, + "ip_container_proxy_add_del": { + "reply": "ip_container_proxy_add_del_reply" + }, + "ip_container_proxy_dump": { + "reply": "ip_container_proxy_details", + "stream": true + }, + "ip_source_and_port_range_check_add_del": { + "reply": "ip_source_and_port_range_check_add_del_reply" + }, + "ip_source_and_port_range_check_interface_add_del": { + "reply": "ip_source_and_port_range_check_interface_add_del_reply" + }, + "sw_interface_ip6_set_link_local_address": { + "reply": "sw_interface_ip6_set_link_local_address_reply" + }, + "sw_interface_ip6_get_link_local_address": { + "reply": "sw_interface_ip6_get_link_local_address_reply" + }, + "ioam_enable": { + "reply": "ioam_enable_reply" + }, + "ioam_disable": { + "reply": "ioam_disable_reply" + }, + "ip_reassembly_set": { + "reply": "ip_reassembly_set_reply" + }, + "ip_reassembly_get": { + "reply": "ip_reassembly_get_reply" + }, + "ip_reassembly_enable_disable": { + "reply": "ip_reassembly_enable_disable_reply" + } + }, + "options": { + "version": "3.0.3" + }, + "aliases": { + "interface_index": { + "type": "u32" + }, + "ip4_address": { + "type": "u8", + "length": 4 + }, + "ip6_address": { + "type": "u8", + "length": 16 + }, + "address_with_prefix": { + "type": "vl_api_prefix_t" + }, + "ip4_address_with_prefix": { + "type": "vl_api_ip4_prefix_t" + }, + "ip6_address_with_prefix": { + "type": "vl_api_ip6_prefix_t" + }, + "mac_address": { + "type": "u8", + "length": 6 + } + }, + "vl_api_version": "0xf2f5f4e" } diff --git a/cmd/govpp/main.go b/cmd/govpp/main.go index 98d5078..1c6b905 100644 --- a/cmd/govpp/main.go +++ b/cmd/govpp/main.go @@ -122,8 +122,8 @@ func showVPPAPI(out io.Writer, apifiles []*vppapi.File) { } imports := fmt.Sprintf("%d apis, %2d types", len(apifile.Imports), len(importedTypes)) path := strings.TrimPrefix(apifile.Path, vppapi.DefaultDir+"/") - types := fmt.Sprintf("%2d enum, %2d alias, %2d struct, %2d union, %2d msg", - len(apifile.EnumTypes), len(apifile.AliasTypes), len(apifile.StructTypes), len(apifile.UnionTypes), len(apifile.Messages)) + types := fmt.Sprintf("%2d enum, %2d enumflag, %2d alias, %2d struct, %2d union, %2d msg", + len(apifile.EnumTypes), len(apifile.EnumflagTypes), len(apifile.AliasTypes), len(apifile.StructTypes), len(apifile.UnionTypes), len(apifile.Messages)) fmt.Fprintf(w, " %s\t%s\t%s\t%s\t%v\t%s\t\n", apifile.Name, strings.Join(options, " "), apifile.CRC, path, imports, types) } -- 2.16.6 From 4459b648e9fb53c34abbf52a00e63ad384fb9ee2 Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Thu, 18 Feb 2021 16:05:30 +0100 Subject: [PATCH 10/16] Added asynchronous connection for stats socket The stats socket now allows an option to connect asynchronously in the same manner as the api socket connection. New method AsyncConnectStats returns a channel where notificaitons of type ConnectionEvent will be sent. Fixed the stats reconnect procedure which sometimes failed to re-eneable the connection. Change-Id: I0bdb19f0d57e3a1ea259b8b1ba0a5e5fa49a09db Signed-off-by: Vladimir Lavor --- adapter/stats_api.go | 1 + adapter/statsclient/statsclient.go | 103 +++++++++++++++++++++++---------- core/stats.go | 115 ++++++++++++++++++++++++++++++------- examples/stats-client/stats_api.go | 33 +++++++++-- 4 files changed, 195 insertions(+), 57 deletions(-) diff --git a/adapter/stats_api.go b/adapter/stats_api.go index 90dbeb3..15c3789 100644 --- a/adapter/stats_api.go +++ b/adapter/stats_api.go @@ -27,6 +27,7 @@ const ( var ( ErrStatsDataBusy = errors.New("stats data busy") ErrStatsDirStale = errors.New("stats dir stale") + ErrStatsDisconnected = errors.New("stats disconnected") ErrStatsAccessFailed = errors.New("stats access failed") ) diff --git a/adapter/statsclient/statsclient.go b/adapter/statsclient/statsclient.go index 9470275..e99d787 100644 --- a/adapter/statsclient/statsclient.go +++ b/adapter/statsclient/statsclient.go @@ -20,20 +20,28 @@ import ( "fmt" "net" "os" + "path/filepath" "regexp" + "sync/atomic" "syscall" "time" + "git.fd.io/govpp.git/adapter" "github.com/fsnotify/fsnotify" "github.com/ftrvxmtrx/fd" logger "github.com/sirupsen/logrus" - - "git.fd.io/govpp.git/adapter" ) const ( // DefaultSocketName is default VPP stats socket file path. DefaultSocketName = adapter.DefaultStatsSocket + + // SocketRetryPeriod is the time period after the socket availability + // will be re-checked + SocketRetryPeriod = 50 * time.Millisecond + + // SocketRetryTimeout is the maximum time for the stats socket + SocketRetryTimeout = 3 * time.Second ) var ( @@ -64,10 +72,12 @@ var _ adapter.StatsAPI = (*StatsClient)(nil) // StatsClient is the pure Go implementation for VPP stats API. type StatsClient struct { - socketPath string + socket string + + headerData []byte - headerData []byte - isConnected bool + // defines the adapter connection state + connected uint32 // to quit socket monitor done chan struct{} @@ -82,17 +92,14 @@ func NewStatsClient(socket string) *StatsClient { socket = DefaultSocketName } return &StatsClient{ - socketPath: socket, + socket: socket, } } // Connect to validated VPP stats socket and start monitoring // socket file changes func (sc *StatsClient) Connect() (err error) { - if sc.isConnected { - return fmt.Errorf("already connected") - } - if err := sc.checkSocketValid(); err != nil { + if err := sc.waitForSocket(); err != nil { return err } sc.done = make(chan struct{}) @@ -100,22 +107,29 @@ func (sc *StatsClient) Connect() (err error) { return err } sc.monitorSocket() - sc.isConnected = true return nil } // Disconnect from the socket, unmap shared memory and terminate // socket monitor func (sc *StatsClient) Disconnect() error { - if !sc.isConnected { - return nil // not connected + if sc.headerData == nil { + return nil } - sc.isConnected = false - close(sc.done) - return sc.disconnect() + if err := syscall.Munmap(sc.headerData); err != nil { + Log.Debugf("unmapping shared memory failed: %v", err) + return fmt.Errorf("unmapping shared memory failed: %v", err) + } + sc.headerData = nil + + Log.Debugf("successfully unmapped shared memory") + return nil } func (sc *StatsClient) ListStats(patterns ...string) ([]string, error) { + if !sc.isConnected() { + return nil, adapter.ErrStatsDisconnected + } accessEpoch := sc.accessStart() if accessEpoch == 0 { return nil, adapter.ErrStatsAccessFailed @@ -149,6 +163,9 @@ func (sc *StatsClient) ListStats(patterns ...string) ([]string, error) { } func (sc *StatsClient) DumpStats(patterns ...string) (entries []adapter.StatEntry, err error) { + if !sc.isConnected() { + return nil, adapter.ErrStatsDisconnected + } accessEpoch := sc.accessStart() if accessEpoch == 0 { return nil, adapter.ErrStatsAccessFailed @@ -192,6 +209,9 @@ func (sc *StatsClient) DumpStats(patterns ...string) (entries []adapter.StatEntr } func (sc *StatsClient) PrepareDir(patterns ...string) (*adapter.StatDir, error) { + if !sc.isConnected() { + return nil, adapter.ErrStatsDisconnected + } dir := new(adapter.StatDir) accessEpoch := sc.accessStart() @@ -241,6 +261,9 @@ func (sc *StatsClient) PrepareDir(patterns ...string) (*adapter.StatDir, error) // UpdateDir refreshes directory data for all counters func (sc *StatsClient) UpdateDir(dir *adapter.StatDir) (err error) { + if !sc.isConnected() { + return adapter.ErrStatsDisconnected + } epoch, _ := sc.GetEpoch() if dir.Epoch != epoch { return adapter.ErrStatsDirStale @@ -281,11 +304,25 @@ func (sc *StatsClient) UpdateDir(dir *adapter.StatDir) (err error) { return nil } -func (sc *StatsClient) checkSocketValid() error { - if _, err := os.Stat(sc.socketPath); os.IsNotExist(err) { - return fmt.Errorf("stats socket file %s does not exist", sc.socketPath) - } else if err != nil { - return fmt.Errorf("stats socket error: %v", err) +// checks the socket existence and waits for it for the designated +// time if it is not available immediately +func (sc *StatsClient) waitForSocket() error { + if _, err := os.Stat(sc.socket); err != nil { + if os.IsNotExist(err) { + ticker := time.NewTicker(SocketRetryPeriod) + for { + select { + case <-ticker.C: + if _, err := os.Stat(sc.socket); err == nil { + return nil + } + case <-time.After(SocketRetryTimeout): + return fmt.Errorf("stats socket file %s is not ready within timeout ", sc.socket) + } + } + } else { + return fmt.Errorf("stats socket error: %v", err) + } } return nil } @@ -295,7 +332,7 @@ func (sc *StatsClient) checkSocketValid() error { func (sc *StatsClient) connect() (ss statSegment, err error) { addr := net.UnixAddr{ Net: "unixpacket", - Name: sc.socketPath, + Name: sc.socket, } Log.Debugf("connecting to: %v", addr) @@ -350,6 +387,9 @@ func (sc *StatsClient) connect() (ss statSegment, err error) { version, minVersion, maxVersion) } + // set connected + atomic.CompareAndSwapUint32(&sc.connected, 0, 1) + return ss, nil } @@ -359,8 +399,8 @@ func (sc *StatsClient) reconnect() (err error) { if err = sc.disconnect(); err != nil { return fmt.Errorf("error disconnecting socket: %v", err) } - if err = sc.checkSocketValid(); err != nil { - return fmt.Errorf("error validating socket: %v", err) + if err = sc.waitForSocket(); err != nil { + return fmt.Errorf("error while waiting on socket: %v", err) } if sc.statSegment, err = sc.connect(); err != nil { return fmt.Errorf("error connecting socket: %v", err) @@ -370,6 +410,9 @@ func (sc *StatsClient) reconnect() (err error) { // disconnect unmaps socket data from the memory and resets the header func (sc *StatsClient) disconnect() error { + if !atomic.CompareAndSwapUint32(&sc.connected, 1, 0) { + return fmt.Errorf("stats client is already disconnected") + } if sc.headerData == nil { return nil } @@ -394,14 +437,10 @@ func (sc *StatsClient) monitorSocket() { for { select { case event := <-watcher.Events: - if event.Op == fsnotify.Remove { + if event.Op == fsnotify.Remove && event.Name == sc.socket { if err := sc.reconnect(); err != nil { Log.Errorf("error occurred during socket reconnect: %v", err) } - // path must be re-added to the watcher - if err = watcher.Add(sc.socketPath); err != nil { - Log.Errorf("failed to add socket address to the watcher: %v", err) - } } case err := <-watcher.Errors: Log.Errorf("socket monitor delivered error event: %v", err) @@ -413,7 +452,7 @@ func (sc *StatsClient) monitorSocket() { } }() - if err := watcher.Add(sc.socketPath); err != nil { + if err := watcher.Add(filepath.Dir(sc.socket)); err != nil { Log.Errorf("failed to add socket address to the watcher: %v", err) } } @@ -496,3 +535,7 @@ func (sc *StatsClient) listIndexesFunc(f func(name []byte) bool) (indexes []uint return indexes, nil } + +func (sc *StatsClient) isConnected() bool { + return atomic.LoadUint32(&sc.connected) == 1 +} diff --git a/core/stats.go b/core/stats.go index f2da494..55c287e 100644 --- a/core/stats.go +++ b/core/stats.go @@ -3,7 +3,6 @@ package core import ( "path" "strings" - "sync/atomic" "time" "git.fd.io/govpp.git/adapter" @@ -11,8 +10,9 @@ import ( ) var ( - RetryUpdateCount = 10 - RetryUpdateDelay = time.Millisecond * 10 + RetryUpdateCount = 10 + RetryUpdateDelay = time.Millisecond * 10 + HealthCheckInterval = time.Second // default health check probe interval ) const ( @@ -76,8 +76,11 @@ const ( type StatsConnection struct { statsClient adapter.StatsAPI - // connected is true if the adapter is connected to VPP - connected uint32 + maxAttempts int // interval for reconnect attempts + recInterval time.Duration // maximum number of reconnect attempts + + connChan chan ConnectionEvent // connection event channel + done chan struct{} // to terminate stats connection watcher errorStatsData *adapter.StatDir nodeStatsData *adapter.StatDir @@ -87,9 +90,20 @@ type StatsConnection struct { memStatsData *adapter.StatDir } -func newStatsConnection(stats adapter.StatsAPI) *StatsConnection { +func newStatsConnection(stats adapter.StatsAPI, attempts int, interval time.Duration) *StatsConnection { + if attempts == 0 { + attempts = DefaultMaxReconnectAttempts + } + if interval == 0 { + interval = DefaultReconnectInterval + } + return &StatsConnection{ statsClient: stats, + maxAttempts: attempts, + recInterval: interval, + connChan: make(chan ConnectionEvent, NotificationChanBufSize), + done: make(chan struct{}), } } @@ -97,28 +111,50 @@ func newStatsConnection(stats adapter.StatsAPI) *StatsConnection { // This call blocks until it is either connected, or an error occurs. // Only one connection attempt will be performed. func ConnectStats(stats adapter.StatsAPI) (*StatsConnection, error) { - c := newStatsConnection(stats) + log.Debug("Connecting to stats..") + c := newStatsConnection(stats, DefaultMaxReconnectAttempts, DefaultReconnectInterval) - if err := c.connectClient(); err != nil { + if err := c.statsClient.Connect(); err != nil { return nil, err } + log.Debugf("Connected to stats.") return c, nil } -func (c *StatsConnection) connectClient() error { - log.Debug("Connecting to stats..") +// AsyncConnectStats connects to the VPP stats socket asynchronously and returns the connection +// handle with state channel. The call is non-blocking and the caller is expected to watch ConnectionEvent +// values from the channel and wait for connect/disconnect events. Connection loop tries to reconnect the +// socket in case the session was disconnected. +func AsyncConnectStats(stats adapter.StatsAPI, attempts int, interval time.Duration) (*StatsConnection, chan ConnectionEvent, error) { + log.Debug("Connecting to stats asynchronously..") + c := newStatsConnection(stats, attempts, interval) - if err := c.statsClient.Connect(); err != nil { - return err - } + go c.connectLoop() - log.Debugf("Connected to stats.") + return c, c.connChan, nil +} - // store connected state - atomic.StoreUint32(&c.connected, 1) +func (c *StatsConnection) connectLoop() { + log.Debug("Asynchronously connecting to stats..") + var reconnectAttempts int - return nil + // loop until connected + for { + if err := c.statsClient.Connect(); err == nil { + c.sendStatsConnEvent(ConnectionEvent{Timestamp: time.Now(), State: Connected}) + break + } else if reconnectAttempts < c.maxAttempts { + reconnectAttempts++ + log.Warnf("connecting stats failed (attempt %d/%d): %v", reconnectAttempts, c.maxAttempts, err) + time.Sleep(c.recInterval) + } else { + c.sendStatsConnEvent(ConnectionEvent{Timestamp: time.Now(), State: Failed, Error: err}) + return + } + } + // start monitoring stats connection state + go c.monitorSocket() } // Disconnect disconnects from Stats API and releases all connection-related resources. @@ -127,14 +163,41 @@ func (c *StatsConnection) Disconnect() { return } if c.statsClient != nil { - c.disconnectClient() + if err := c.statsClient.Disconnect(); err != nil { + log.Debugf("disconnecting stats client failed: %v", err) + } } + close(c.connChan) + close(c.done) } -func (c *StatsConnection) disconnectClient() { - if atomic.CompareAndSwapUint32(&c.connected, 1, 0) { - if err := c.statsClient.Disconnect(); err != nil { - log.Debugf("disconnecting stats client failed: %v", err) +func (c *StatsConnection) monitorSocket() { + var state, lastState ConnectionState + ticker := time.NewTicker(HealthCheckInterval) + + for { + select { + case <-ticker.C: + _, err := c.statsClient.ListStats(SystemStats_Heartbeat) + state = Connected + if err == adapter.ErrStatsDataBusy { + state = NotResponding + } + if err == adapter.ErrStatsDisconnected { + state = Disconnected + } + if err == adapter.ErrStatsAccessFailed { + state = Failed + } + if state == lastState { + continue + } + lastState = state + c.sendStatsConnEvent(ConnectionEvent{Timestamp: time.Now(), State: state, Error: err}) + case <-c.done: + log.Debugf("health check watcher closed") + c.sendStatsConnEvent(ConnectionEvent{Timestamp: time.Now(), State: Disconnected, Error: nil}) + break } } } @@ -498,3 +561,11 @@ func (c *StatsConnection) GetMemoryStats(memStats *api.MemoryStats) (err error) } return nil } + +func (c *StatsConnection) sendStatsConnEvent(event ConnectionEvent) { + select { + case c.connChan <- event: + default: + log.Warn("Stats connection state channel is full, discarding value.") + } +} diff --git a/examples/stats-client/stats_api.go b/examples/stats-client/stats_api.go index b1846a6..66dd451 100644 --- a/examples/stats-client/stats_api.go +++ b/examples/stats-client/stats_api.go @@ -39,6 +39,7 @@ var ( statsSocket = flag.String("socket", statsclient.DefaultSocketName, "Path to VPP stats socket") dumpAll = flag.Bool("all", false, "Dump all stats including ones with zero values") pollPeriod = flag.Duration("period", time.Second*5, "Polling interval period") + async = flag.Bool("async", false, "Use asynchronous connection") ) func init() { @@ -58,11 +59,33 @@ func main() { patterns = flag.Args()[1:] } - client := statsclient.NewStatsClient(*statsSocket) - - c, err := core.ConnectStats(client) - if err != nil { - log.Fatalln("Connecting failed:", err) + var ( + client *statsclient.StatsClient + c *core.StatsConnection + err error + ) + + if *async { + var statsChan chan core.ConnectionEvent + client = statsclient.NewStatsClient(*statsSocket) + c, statsChan, err = core.AsyncConnectStats(client, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval) + if err != nil { + log.Fatalln("Asynchronous connecting failed:", err) + } + select { + case e := <-statsChan: + if e.State == core.Connected { + // OK + } else { + log.Fatalf("VPP stats asynchronous connection failed: %s\n", e.State.String()) + } + } + } else { + client = statsclient.NewStatsClient(*statsSocket) + c, err = core.ConnectStats(client) + if err != nil { + log.Fatalln("Connecting failed:", err) + } } defer c.Disconnect() -- 2.16.6 From 4e16c7100cc7f8dddca051ff393460d7a1a77c98 Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Tue, 2 Mar 2021 11:45:37 +0100 Subject: [PATCH 11/16] Provide error counters per worker for statsclient Instead of a single value, the ErrorCounter now contains an array representing couter values per workers. Change-Id: I2b869e5b228bcb3e155b1fef08a4cd00e7d2e16a Signed-off-by: Vladimir Lavor --- adapter/stats_api.go | 14 +++++++++++--- adapter/statsclient/statseg_v1.go | 8 ++++---- adapter/statsclient/statseg_v2.go | 8 ++++---- adapter/vppapiclient/stat_client.go | 2 +- api/stats.go | 2 +- core/stats.go | 6 +++++- examples/stats-client/stats_api.go | 9 +++++++-- 7 files changed, 33 insertions(+), 16 deletions(-) diff --git a/adapter/stats_api.go b/adapter/stats_api.go index 15c3789..4b398f5 100644 --- a/adapter/stats_api.go +++ b/adapter/stats_api.go @@ -126,8 +126,8 @@ type Stat interface { // ScalarStat represents stat for ScalarIndex. type ScalarStat float64 -// ErrorStat represents stat for ErrorIndex. -type ErrorStat Counter +// ErrorStat represents stat for ErrorIndex. The array represents workers. +type ErrorStat []Counter // SimpleCounterStat represents stat for SimpleCounterVector. // The outer array represents workers and the inner array represents interface/node/.. indexes. @@ -154,7 +154,15 @@ func (s ScalarStat) IsZero() bool { return s == 0 } func (s ErrorStat) IsZero() bool { - return s == 0 + if s == nil { + return true + } + for _, ss := range s { + if ss != 0 { + return false + } + } + return true } func (s SimpleCounterStat) IsZero() bool { if s == nil { diff --git a/adapter/statsclient/statseg_v1.go b/adapter/statsclient/statseg_v1.go index 3d45201..38f51bd 100644 --- a/adapter/statsclient/statseg_v1.go +++ b/adapter/statsclient/statseg_v1.go @@ -113,7 +113,7 @@ func (ss *statSegmentV1) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { _, errOffset, _ := ss.getOffsets() offsetVector := unsafe.Pointer(&ss.sharedHeader[errOffset]) - var errData adapter.Counter + var errData []adapter.Counter vecLen := *(*uint32)(vectorLen(offsetVector)) for i := uint32(0); i < vecLen; i++ { @@ -121,7 +121,7 @@ func (ss *statSegmentV1) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { offset := uintptr(cb) + uintptr(dirEntry.unionData)*unsafe.Sizeof(adapter.Counter(0)) debugf("error index, cb: %d, offset: %d", cb, offset) val := *(*adapter.Counter)(statSegPointer(unsafe.Pointer(&ss.sharedHeader[0]), offset)) - errData += val + errData = append(errData, val) } return adapter.ErrorStat(errData) @@ -239,14 +239,14 @@ func (ss *statSegmentV1) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte _, errOffset, _ := ss.getOffsets() offsetVector := unsafe.Pointer(&ss.sharedHeader[errOffset]) - var errData adapter.Counter + var errData []adapter.Counter vecLen := *(*uint32)(vectorLen(unsafe.Pointer(&ss.sharedHeader[errOffset]))) for i := uint32(0); i < vecLen; i++ { cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0)))) offset := uintptr(cb) + uintptr(dirEntry.unionData)*unsafe.Sizeof(adapter.Counter(0)) val := *(*adapter.Counter)(statSegPointer(unsafe.Pointer(&ss.sharedHeader[0]), offset)) - errData += val + errData = append(errData, val) } *stat = adapter.ErrorStat(errData) diff --git a/adapter/statsclient/statseg_v2.go b/adapter/statsclient/statseg_v2.go index d52077f..68411f9 100644 --- a/adapter/statsclient/statseg_v2.go +++ b/adapter/statsclient/statseg_v2.go @@ -105,7 +105,7 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { return nil } vecLen := *(*uint32)(vectorLen(dirVector)) - var errData adapter.Counter + var errData []adapter.Counter for i := uint32(0); i < vecLen; i++ { cb := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0))) cbVal := ss.adjust(vectorLen(cb)) @@ -115,7 +115,7 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { } offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(adapter.Counter(0)) val := *(*adapter.Counter)(statSegPointer(cbVal, offset)) - errData += val + errData = append(errData, val) } return adapter.ErrorStat(errData) @@ -224,7 +224,7 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte return nil } vecLen := *(*uint32)(vectorLen(dirVector)) - var errData adapter.Counter + var errData []adapter.Counter for i := uint32(0); i < vecLen; i++ { cb := statSegPointer(dirVector, uintptr(i+1)*unsafe.Sizeof(uint64(0))) cbVal := ss.adjust(vectorLen(cb)) @@ -234,7 +234,7 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte } offset := uintptr(dirEntry.unionData) * unsafe.Sizeof(adapter.Counter(0)) val := *(*adapter.Counter)(statSegPointer(cbVal, offset)) - errData += val + errData = append(errData, val) } *stat = adapter.ErrorStat(errData) diff --git a/adapter/vppapiclient/stat_client.go b/adapter/vppapiclient/stat_client.go index bf19c45..a124f59 100644 --- a/adapter/vppapiclient/stat_client.go +++ b/adapter/vppapiclient/stat_client.go @@ -130,7 +130,7 @@ func (c *statClient) DumpStats(patterns ...string) (stats []adapter.StatEntry, e stat.Data = adapter.ScalarStat(C.govpp_stat_segment_data_get_scalar_value(&v)) case adapter.ErrorIndex: - stat.Data = adapter.ErrorStat(C.govpp_stat_segment_data_get_error_value(&v)) + stat.Data = adapter.ErrorStat([]adapter.Counter{adapter.Counter(C.govpp_stat_segment_data_get_error_value(&v))}) case adapter.SimpleCounterVector: length := int(C.govpp_stat_segment_vec_len(unsafe.Pointer(C.govpp_stat_segment_data_get_simple_counter(&v)))) diff --git a/api/stats.go b/api/stats.go index 9e1ba75..3a72c6c 100644 --- a/api/stats.go +++ b/api/stats.go @@ -98,7 +98,7 @@ type ErrorStats struct { type ErrorCounter struct { CounterName string - Value uint64 + Values []uint64 } // BufferStats represents statistics per buffer pool. diff --git a/core/stats.go b/core/stats.go index 55c287e..3218f1e 100644 --- a/core/stats.go +++ b/core/stats.go @@ -305,7 +305,11 @@ func (c *StatsConnection) GetErrorStats(errorStats *api.ErrorStats) (err error) continue } if errStat, ok := stat.Data.(adapter.ErrorStat); ok { - errorStats.Errors[i].Value = uint64(errStat) + values := make([]uint64, len(errStat)) + for j, errStatW := range errStat { + values[j] = uint64(errStatW) + } + errorStats.Errors[i].Values = values } } diff --git a/examples/stats-client/stats_api.go b/examples/stats-client/stats_api.go index 66dd451..0ccfa89 100644 --- a/examples/stats-client/stats_api.go +++ b/examples/stats-client/stats_api.go @@ -137,10 +137,15 @@ func main() { } n := 0 for _, counter := range stats.Errors { - if skipZeros && counter.Value == 0 { + var sum uint32 + for _, valuePerWorker := range counter.Values { + sum += uint32(valuePerWorker) + } + + if skipZeros && sum == 0 { continue } - fmt.Printf(" - %v\n", counter) + fmt.Printf(" - %v %d (per worker: %v)\n", counter.CounterName, sum, counter.Values) n++ } fmt.Printf("Listed %d (%d) error counters\n", n, len(stats.Errors)) -- 2.16.6 From 8ff6fc436ce5a5be694e7d4cc9e56ded10184d80 Mon Sep 17 00:00:00 2001 From: Alexander Demidenko Date: Fri, 19 Mar 2021 12:11:21 +0700 Subject: [PATCH 12/16] Fix panic during disconnection Sometimes there are situations when the callback is called during the processing of the disconnect. To avoid problems with races (for example, when using race-detector), use atomic pointers. Signed-off-by: Alexander Demidenko Change-Id: Ic825872ac620d68a191bc88889a096b15ea57464 --- adapter/vppapiclient/vppapiclient.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/adapter/vppapiclient/vppapiclient.go b/adapter/vppapiclient/vppapiclient.go index 977f32d..7297590 100644 --- a/adapter/vppapiclient/vppapiclient.go +++ b/adapter/vppapiclient/vppapiclient.go @@ -29,6 +29,7 @@ import ( "os" "path/filepath" "reflect" + "sync/atomic" "time" "unsafe" @@ -52,7 +53,7 @@ const ( // global VPP binary API client, library vppapiclient only supports // single connection at a time -var globalVppClient *vppClient +var globalVppClient unsafe.Pointer // stubVppClient is the default implementation of the VppAPI. type vppClient struct { @@ -76,7 +77,8 @@ func NewVppClientWithInputQueueSize(shmPrefix string, inputQueueSize uint16) ada // Connect connects the process to VPP. func (a *vppClient) Connect() error { - if globalVppClient != nil { + h := (*vppClient)(atomic.LoadPointer(&globalVppClient)) + if h != nil { return fmt.Errorf("already connected to binary API, disconnect first") } @@ -92,19 +94,17 @@ func (a *vppClient) Connect() error { return fmt.Errorf("connecting to VPP binary API failed (rc=%v)", rc) } - globalVppClient = a + atomic.StorePointer(&globalVppClient, unsafe.Pointer(a)) return nil } // Disconnect disconnects the process from VPP. func (a *vppClient) Disconnect() error { - globalVppClient = nil - + atomic.StorePointer(&globalVppClient, nil) rc := C.govpp_disconnect() if rc != 0 { return fmt.Errorf("disconnecting from VPP binary API failed (rc=%v)", rc) } - return nil } @@ -187,9 +187,12 @@ func (a *vppClient) WaitReady() error { //export go_msg_callback func go_msg_callback(msgID C.uint16_t, data unsafe.Pointer, size C.size_t) { + h := (*vppClient)(atomic.LoadPointer(&globalVppClient)) + if h == nil { + return + } // convert unsafe.Pointer to byte slice sliceHeader := &reflect.SliceHeader{Data: uintptr(data), Len: int(size), Cap: int(size)} byteSlice := *(*[]byte)(unsafe.Pointer(sliceHeader)) - - globalVppClient.msgCallback(uint16(msgID), byteSlice) + h.msgCallback(uint16(msgID), byteSlice) } -- 2.16.6 From 671f16c7b4ca24788fc503b4344fa22306548a3b Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Mon, 22 Mar 2021 13:34:37 +0100 Subject: [PATCH 13/16] Add statsclient options and fix wait for socket Change-Id: Ib5674fee5862a2b16f4b0044b6f6af533a7b6b33 Signed-off-by: Vladimir Lavor --- adapter/statsclient/statsclient.go | 54 +++++++++++++++++++++++++++++++------- examples/stats-client/stats_api.go | 3 ++- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/adapter/statsclient/statsclient.go b/adapter/statsclient/statsclient.go index e99d787..b2d91db 100644 --- a/adapter/statsclient/statsclient.go +++ b/adapter/statsclient/statsclient.go @@ -36,12 +36,12 @@ const ( // DefaultSocketName is default VPP stats socket file path. DefaultSocketName = adapter.DefaultStatsSocket - // SocketRetryPeriod is the time period after the socket availability + // DefaultSocketRetryPeriod is the time period after the socket availability // will be re-checked - SocketRetryPeriod = 50 * time.Millisecond + DefaultSocketRetryPeriod = 50 * time.Millisecond - // SocketRetryTimeout is the maximum time for the stats socket - SocketRetryTimeout = 3 * time.Second + // DefaultSocketRetryTimeout is the maximum time for the stats socket + DefaultSocketRetryTimeout = 3 * time.Second ) var ( @@ -72,7 +72,9 @@ var _ adapter.StatsAPI = (*StatsClient)(nil) // StatsClient is the pure Go implementation for VPP stats API. type StatsClient struct { - socket string + socket string + retryPeriod time.Duration + retryTimeout time.Duration headerData []byte @@ -85,15 +87,44 @@ type StatsClient struct { statSegment } +// Option is a StatsClient option +type Option func(*StatsClient) + +// SetSocketRetryPeriod is and optional parameter to define a custom +// retry period while waiting for the VPP socket +func SetSocketRetryPeriod(t time.Duration) Option { + return func(c *StatsClient) { + c.retryPeriod = t + } +} + +// SetSocketRetryTimeout is and optional parameter to define a custom +// timeout while waiting for the VPP socket +func SetSocketRetryTimeout(t time.Duration) Option { + return func(c *StatsClient) { + c.retryTimeout = t + } +} + // NewStatsClient returns a new StatsClient using socket. // If socket is empty string DefaultSocketName is used. -func NewStatsClient(socket string) *StatsClient { +func NewStatsClient(socket string, options ...Option) *StatsClient { if socket == "" { socket = DefaultSocketName } - return &StatsClient{ + s := &StatsClient{ socket: socket, } + for _, option := range options { + option(s) + } + if s.retryPeriod == 0 { + s.retryPeriod = DefaultSocketRetryPeriod + } + if s.retryTimeout == 0 { + s.retryTimeout = DefaultSocketRetryTimeout + } + return s } // Connect to validated VPP stats socket and start monitoring @@ -309,15 +340,18 @@ func (sc *StatsClient) UpdateDir(dir *adapter.StatDir) (err error) { func (sc *StatsClient) waitForSocket() error { if _, err := os.Stat(sc.socket); err != nil { if os.IsNotExist(err) { - ticker := time.NewTicker(SocketRetryPeriod) + n := time.Now() + ticker := time.NewTicker(sc.retryPeriod) + timeout := time.After(sc.retryTimeout) for { select { case <-ticker.C: if _, err := os.Stat(sc.socket); err == nil { return nil } - case <-time.After(SocketRetryTimeout): - return fmt.Errorf("stats socket file %s is not ready within timeout ", sc.socket) + case <-timeout: + return fmt.Errorf("stats socket file %s is not ready within timeout (after %.2f s) ", + sc.socket, time.Since(n).Seconds()) } } } else { diff --git a/examples/stats-client/stats_api.go b/examples/stats-client/stats_api.go index 0ccfa89..fa39b54 100644 --- a/examples/stats-client/stats_api.go +++ b/examples/stats-client/stats_api.go @@ -67,7 +67,8 @@ func main() { if *async { var statsChan chan core.ConnectionEvent - client = statsclient.NewStatsClient(*statsSocket) + client = statsclient.NewStatsClient(*statsSocket, statsclient.SetSocketRetryPeriod(1*time.Second), + statsclient.SetSocketRetryTimeout(10*time.Second)) c, statsChan, err = core.AsyncConnectStats(client, core.DefaultMaxReconnectAttempts, core.DefaultReconnectInterval) if err != nil { log.Fatalln("Asynchronous connecting failed:", err) -- 2.16.6 From 1d574a786662498896d0e5e089f3c312910e0ffa Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Thu, 15 Apr 2021 14:31:26 +0200 Subject: [PATCH 14/16] fix INFO.yaml Change-Id: Id18086b2d1df911cdc5d0092e48fd6034ddc414e Signed-off-by: Vladimir Lavor --- INFO.yaml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/INFO.yaml b/INFO.yaml index 6f6a76b..aa707c2 100644 --- a/INFO.yaml +++ b/INFO.yaml @@ -6,7 +6,7 @@ lifecycle_state: 'Incubation' project_lead: &govpp_ptl name: 'Ondrej Fabry' email: 'ofabry@cisco.com' - id: 'ofabry' + id: 'ondrej-fabry' primary_contact: *govpp_ptl issue_tracking: type: 'jira' @@ -27,7 +27,7 @@ committers: - name: 'Ondrej Fabry' company: 'cisco' email: 'ofabry@cisco.com' - id: 'ofabry' + id: 'ondrej-fabry' - name: 'Jan Medved' company: 'cisco' email: 'jmedved@cisco.com' @@ -36,10 +36,6 @@ committers: company: 'cisco' email: 'raszabo@cisco.com' id: 'raszabo' - - name: 'Nikos Bregiannis' - company: 'cisco' - email: 'nbregian@cisco.com' - id: 'nbregian' - name: 'Vladimir Lavor' company: 'cisco' email: 'vlavor@cisco.com' @@ -52,5 +48,5 @@ tsc: name: '' link: '' - type: 'promotion' - name: 'Vladimir Lavor' - link: 'https://lists.fd.io/g/govpp-dev/topic/77726308#64,https://lists.fd.io/g/govpp-dev/message/69' + name: '' + link: '' -- 2.16.6 From d0b973030fe07dc7875da72f5ebe42d8bd9544b1 Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Thu, 11 Mar 2021 15:27:02 +0100 Subject: [PATCH 15/16] Recognize stat_dir_type_empty Do not to handle empty stats directory as unknown. Empty dir's value is always treated as zero. Change-Id: I40a3829675c5b7c9da80ceaa7e2d0a87fe02dc7c Signed-off-by: Vladimir Lavor --- adapter/stats_api.go | 10 ++++++++++ adapter/statsclient/statseg_v2.go | 1 + 2 files changed, 11 insertions(+) diff --git a/adapter/stats_api.go b/adapter/stats_api.go index 4b398f5..d15dee8 100644 --- a/adapter/stats_api.go +++ b/adapter/stats_api.go @@ -60,6 +60,7 @@ const ( CombinedCounterVector StatType = 3 ErrorIndex StatType = 4 NameVector StatType = 5 + Empty StatType = 6 ) func (d StatType) String() string { @@ -74,6 +75,8 @@ func (d StatType) String() string { return "ErrorIndex" case NameVector: return "NameVector" + case Empty: + return "Empty" } return fmt.Sprintf("UnknownStatType(%d)", d) } @@ -144,11 +147,15 @@ type CombinedCounterStat [][]CombinedCounter // NameStat represents stat for NameVector. type NameStat []Name +// EmptyStat represents removed counter directory +type EmptyStat string + func (ScalarStat) isStat() {} func (ErrorStat) isStat() {} func (SimpleCounterStat) isStat() {} func (CombinedCounterStat) isStat() {} func (NameStat) isStat() {} +func (EmptyStat) isStat() {} func (s ScalarStat) IsZero() bool { return s == 0 @@ -204,6 +211,9 @@ func (s NameStat) IsZero() bool { } return true } +func (s EmptyStat) IsZero() bool { + return true +} // ReduceSimpleCounterStatIndex returns reduced SimpleCounterStat s for index i. func ReduceSimpleCounterStatIndex(s SimpleCounterStat, i int) uint64 { diff --git a/adapter/statsclient/statseg_v2.go b/adapter/statsclient/statseg_v2.go index 68411f9..08467c1 100644 --- a/adapter/statsclient/statseg_v2.go +++ b/adapter/statsclient/statseg_v2.go @@ -202,6 +202,7 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { return adapter.NameStat(data) case statDirEmpty: + return adapter.EmptyStat("") // no-op default: -- 2.16.6 From a6607d9c1ba37320984c13580c932076cbff6dd6 Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Thu, 11 Mar 2021 10:13:17 +0100 Subject: [PATCH 16/16] added api message options In case the API message contains option of type deprecated or in-progress, the information is displayed in the generated code in a form of additional comment: // InProgress: // Deprecated: In case the is not provided, a generic message is shown. Possible future use to automatically search whether such messages are in use. Change-Id: Icf87cc9a2fe6bf31f7555320255c9f0736add6e1 Signed-off-by: Vladimir Lavor --- binapigen/generate.go | 31 +++++++++++++++++++++++++++++++ binapigen/vppapi/api_schema.go | 7 ++++--- binapigen/vppapi/parse_json.go | 33 +++++++++++++++++++++++++++------ 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/binapigen/generate.go b/binapigen/generate.go index a2f941a..2ad3eb3 100644 --- a/binapigen/generate.go +++ b/binapigen/generate.go @@ -41,6 +41,19 @@ const ( fieldUnionData = "XXX_UnionData" // name for the union data field ) +// option keys +const ( + msgStatus = "status" + msgDeprecated = "deprecated" + msgInProgress = "in_progress" +) + +// generated option messages +const ( + deprecatedMsg = "the message will be removed in the future versions" + inProgressMsg = "the message form may change in the future versions" +) + func GenerateAPI(gen *Generator, file *File) *GenFile { logf("----------------------------") logf(" Generate API - %s", file.Desc.Name) @@ -145,6 +158,23 @@ func genTypeComment(g *GenFile, goName string, vppName string, objKind string) { g.P("// ", goName, " defines ", objKind, " '", vppName, "'.") } +func genTypeOptionComment(g *GenFile, options map[string]string) { + // all messages for API versions < 1.0.0 are in_progress by default + if msg, ok := options[msgInProgress]; ok || options[msgStatus] == msgInProgress || + len(g.file.Version) > 1 && g.file.Version[0:2] == "0." { + if msg == "" { + msg = inProgressMsg + } + g.P("// InProgress: ", msg) + } + if msg, ok := options[msgDeprecated]; ok || options[msgStatus] == msgDeprecated { + if msg == "" { + msg = deprecatedMsg + } + g.P("// Deprecated: ", msg) + } +} + func genEnum(g *GenFile, enum *Enum) { logf("gen ENUM %s (%s) - %d entries", enum.GoName, enum.Name, len(enum.Entries)) @@ -453,6 +483,7 @@ func genMessage(g *GenFile, msg *Message) { logf("gen MESSAGE %s (%s) - %d fields", msg.GoName, msg.Name, len(msg.Fields)) genTypeComment(g, msg.GoIdent.GoName, msg.Name, "message") + genTypeOptionComment(g, msg.Options) // generate message definition if len(msg.Fields) == 0 { diff --git a/binapigen/vppapi/api_schema.go b/binapigen/vppapi/api_schema.go index e1c180e..4dd0ac9 100644 --- a/binapigen/vppapi/api_schema.go +++ b/binapigen/vppapi/api_schema.go @@ -62,9 +62,10 @@ type ( } Message struct { - Name string - Fields []Field - CRC string + Name string + Fields []Field + CRC string + Options map[string]string } Field struct { diff --git a/binapigen/vppapi/parse_json.go b/binapigen/vppapi/parse_json.go index a2ca257..ed7bcad 100644 --- a/binapigen/vppapi/parse_json.go +++ b/binapigen/vppapi/parse_json.go @@ -46,10 +46,11 @@ const ( fileServices = "services" fileImports = "imports" // type keys - messageCrc = "crc" - enumType = "enumtype" - aliasLength = "length" - aliasType = "type" + messageCrc = "crc" + messageOptions = "options" + enumType = "enumtype" + aliasLength = "length" + aliasType = "type" // service serviceReply = "reply" serviceStream = "stream" @@ -375,10 +376,30 @@ func parseMessage(msgNode *jsongo.Node) (*Message, error) { if !ok { return nil, fmt.Errorf("message crc invalid or missing") } + var msgOpts map[string]string + msgOptsNode := msgNode.At(msgNode.Len() - 1).Map(messageOptions) + if msgOptsNode.GetType() == jsongo.TypeMap { + msgOpts = make(map[string]string) + for _, opt := range msgOptsNode.GetKeys() { + if _, ok := opt.(string); !ok { + logf("invalid message option key, expected string") + continue + } + msgOpts[opt.(string)] = "" + if msgOptsNode.At(opt).Get() != nil { + if optMsgStr, ok := msgOptsNode.At(opt).Get().(string); ok { + msgOpts[opt.(string)] = optMsgStr + } else { + logf("invalid message option value, expected string") + } + } + } + } msg := Message{ - Name: msgName, - CRC: msgCRC, + Name: msgName, + CRC: msgCRC, + Options: msgOpts, } // loop through message fields, skip first (name) and last (crc) -- 2.16.6