package main import ( "flag" "fmt" "log" "git.fd.io/govpp.git" "git.fd.io/govpp.git/adapter" "git.fd.io/govpp.git/adapter/vppapiclient" "git.fd.io/govpp.git/api" "git.fd.io/govpp.git/core" "git.fd.io/govpp.git/examples/bin_api/interfaces" "git.fd.io/govpp.git/examples/bin_api/vpe" ) ////////////////////////////////////// ///////// Data structs /////////// ////////////////////////////////////// const defaultStatsSocketPath = "/run/vpp/stats.sock" const defaultShmPrefix = "" func parseMacAddress(l2Address []byte, l2AddressLength uint32) string { var mac string for i := uint32(0); i < l2AddressLength; i++ { mac += fmt.Sprintf("%02x", l2Address[i]) if i < l2AddressLength-1 { mac += ":" } } return mac } type interfaceStats struct { TxBytes uint64 TxPackets uint64 TxErrors uint64 RxBytes uint64 RxPackets uint64 RxErrors uint64 Drops uint64 Punts uint64 } type vppInterface struct { interfaces.SwInterfaceDetails Stats interfaceStats } type vppConnector struct { statsSocketPath string shmPrefix string conn *core.Connection api api.Channel stats adapter.StatsAPI VppDetails vpe.ShowVersionReply Interfaces []*vppInterface } ////////////////////////////////////// ///////// VPP workflow /////////// ////////////////////////////////////// func (v *vppConnector) getVppVersion() error { if err := v.api.SendRequest(&vpe.ShowVersion{}).ReceiveReply(&v.VppDetails); err != nil { return fmt.Errorf("failed to fetch vpp version: %v", err) } return nil } func (v *vppConnector) getInterfaces() error { ifCtx := v.api.SendMultiRequest(&interfaces.SwInterfaceDump{}) for { ifDetails := interfaces.SwInterfaceDetails{} stop, err := ifCtx.ReceiveReply(&ifDetails) if err != nil { return fmt.Errorf("failed to fetch vpp interface: %v", err) } if stop { break } v.Interfaces = append(v.Interfaces, &vppInterface{SwInterfaceDetails: ifDetails}) } return nil } func (v *vppConnector) connect() (err error) { if v.conn, err = govpp.Connect(v.shmPrefix); err != nil { return fmt.Errorf("failed to connect to vpp: %v", err) } if v.api, err = v.conn.NewAPIChannel(); err != nil { return fmt.Errorf("failed to create api channel: %v", err) } v.stats = vppapiclient.NewStatClient(v.statsSocketPath) if err = v.stats.Connect(); err != nil { return fmt.Errorf("failed to connect to Stats adapter: %v", err) } return } func (v *vppConnector) disconnect() { if v.stats != nil { v.stats.Disconnect() } if v.conn != nil { v.conn.Disconnect() } } func (v *vppConnector) reduceCombinedCounters(stat *adapter.StatEntry) *[]adapter.CombinedCounter { counters := stat.Data.(adapter.CombinedCounterStat) stats := make([]adapter.CombinedCounter, len(v.Interfaces)) for _, workerStats := range counters { for i := 0; i < len(v.Interfaces); i++ { stats[i].Bytes += workerStats[i].Bytes stats[i].Packets += workerStats[i].Packets } } return &stats } func (v *vppConnector) reduceSimpleCounters(stat *adapter.StatEntry) *[]adapter.Counter { counters := stat.Data.(adapter.SimpleCounterStat) stats := make([]adapter.Counter, len(v.Interfaces)) for _, workerStats := range counters { for i := 0; i < len(v.Interfaces); i++ { stats[i] += workerStats[i] } } return &stats } func (v *vppConnector) getStatsForAllInterfaces() error { statsDump, err := v.stats.DumpStats("/if") if err != nil { return fmt.Errorf("failed to dump vpp Stats: %v", err) } stats := func(i int) *interfaceStats { return &v.Interfaces[uint32(i)].Stats } for _, stat := range statsDump { switch stat.Name { case "/if/tx": { for i, counter := range *v.reduceCombinedCounters(stat) { stats(i).TxBytes = uint64(counter.Bytes) stats(i).TxPackets = uint64(counter.Packets) } } case "/if/rx": { for i, counter := range *v.reduceCombinedCounters(stat) { stats(i).RxBytes = uint64(counter.Bytes) stats(i).RxPackets = uint64(counter.Packets) } } case "/if/tx-error": { for i, counter := range *v.reduceSimpleCounters(stat) { stats(i).TxErrors = uint64(counter) } } case "/if/rx-error": { for i, counter := range *v.reduceSimpleCounters(stat) { stats(i).RxErrors = uint64(counter) } } case "/if/drops": { for i, counter := range *v.reduceSimpleCounters(stat) { stats(i).Drops = uint64(counter) } } case "/if/punt": { for i, counter := range *v.reduceSimpleCounters(stat) { stats(i).Punts = uint64(counter) } } } } return nil } ////////////////////////////////////// func main() { statsSocketPathPtr := flag.String("stats_socket_path", defaultStatsSocketPath, "Path to vpp stats socket") shmPrefixPtr := flag.String("shm_prefix", defaultShmPrefix, "Shared memory prefix (advanced)") flag.Parse() vppConn := &vppConnector{statsSocketPath: *statsSocketPathPtr, shmPrefix: *shmPrefixPtr} defer vppConn.disconnect() if err := vppConn.connect(); err != nil { log.Fatalln(err) } if err := vppConn.getVppVersion(); err != nil { log.Fatalln(err) } if err := vppConn.getInterfaces(); err != nil { log.Fatalln(err) } if err := vppConn.getStatsForAllInterfaces(); err != nil { log.Fatalln(err) } jsonString, err := dumpToJSONString(vppConn) if err != nil { log.Fatalln(err) } fmt.Println(jsonString) }