go.ligato.io/vpp-agent/v3@v3.5.0/plugins/telemetry/stats_poller.go (about) 1 package telemetry 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 govppapi "go.fd.io/govpp/api" 9 "go.ligato.io/cn-infra/v2/logging" 10 "google.golang.org/grpc/codes" 11 "google.golang.org/grpc/status" 12 "google.golang.org/protobuf/proto" 13 14 "go.ligato.io/vpp-agent/v3/plugins/telemetry/vppcalls" 15 "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx" 16 "go.ligato.io/vpp-agent/v3/proto/ligato/configurator" 17 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp" 18 vpp_interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces" 19 ) 20 21 type statsPollerServer struct { 22 configurator.UnimplementedStatsPollerServiceServer 23 24 handler vppcalls.TelemetryVppAPI 25 ifIndex ifaceidx.IfaceMetadataIndex 26 27 log logging.Logger 28 } 29 30 func (s *statsPollerServer) PollStats(req *configurator.PollStatsRequest, svr configurator.StatsPollerService_PollStatsServer) error { 31 if req.GetPeriodSec() == 0 && req.GetNumPolls() > 1 { 32 return status.Error(codes.InvalidArgument, "period must be > 0 if number of polls is > 1") 33 } 34 if s.handler == nil { 35 return status.Errorf(codes.Unavailable, "VPP telemetry handler not available") 36 } 37 38 ctx := svr.Context() 39 40 streamStats := func(pollSeq uint32) (err error) { 41 vppStatsCh := make(chan *vpp.Stats) 42 go func() { 43 err = s.streamVppStats(ctx, vppStatsCh) 44 close(vppStatsCh) 45 }() 46 for vppStats := range vppStatsCh { 47 VppStats := proto.Clone(vppStats).(*vpp.Stats) 48 s.log.Debugf("sending vpp stats: %v", VppStats) 49 50 if err := svr.Send(&configurator.PollStatsResponse{ 51 PollSeq: pollSeq, 52 Stats: &configurator.Stats{ 53 Stats: &configurator.Stats_VppStats{VppStats: VppStats}, 54 }, 55 }); err != nil { 56 s.log.Errorf("sending stats failed: %v", err) 57 return nil 58 } 59 } 60 if err != nil { 61 s.log.Errorf("polling vpp stats failed: %v", err) 62 return err 63 } 64 return nil 65 } 66 67 if req.GetPeriodSec() == 0 { 68 return streamStats(0) 69 } 70 71 period := time.Duration(req.GetPeriodSec()) * time.Second 72 s.log.Debugf("start polling stats every %v", period) 73 74 tick := time.NewTicker(period) 75 defer tick.Stop() 76 77 for pollSeq := uint32(1); ; pollSeq++ { 78 s.log.WithField("seq", pollSeq).Debugf("polling stats..") 79 80 if err := streamStats(pollSeq); err != nil { 81 return err 82 } 83 84 if req.GetNumPolls() > 0 && pollSeq >= req.GetNumPolls() { 85 s.log.Debugf("reached %d pollings", req.GetNumPolls()) 86 return nil 87 } 88 89 select { 90 case <-tick.C: 91 // period passed 92 case <-ctx.Done(): 93 return ctx.Err() 94 } 95 } 96 } 97 98 func (s *statsPollerServer) streamVppStats(ctx context.Context, ch chan *vpp.Stats) error { 99 ifStats, err := s.handler.GetInterfaceStats(ctx) 100 if err != nil { 101 return err 102 } else if ifStats == nil { 103 return fmt.Errorf("interface stats not avaiable") 104 } 105 106 s.log.Debugf("streaming %d interface stats", len(ifStats.Interfaces)) 107 108 for _, iface := range ifStats.Interfaces { 109 name, _, exists := s.ifIndex.LookupBySwIfIndex(iface.InterfaceIndex) 110 if !exists { 111 // fallback to internal name 112 name = iface.InterfaceName 113 } 114 vppStats := &vpp.Stats{ 115 Interface: &vpp_interfaces.InterfaceStats{ 116 Name: name, 117 Rx: convertInterfaceCombined(iface.Rx), 118 Tx: convertInterfaceCombined(iface.Tx), 119 RxUnicast: convertInterfaceCombined(iface.RxUnicast), 120 RxMulticast: convertInterfaceCombined(iface.RxMulticast), 121 RxBroadcast: convertInterfaceCombined(iface.RxBroadcast), 122 TxUnicast: convertInterfaceCombined(iface.TxUnicast), 123 TxMulticast: convertInterfaceCombined(iface.TxMulticast), 124 TxBroadcast: convertInterfaceCombined(iface.TxBroadcast), 125 RxError: iface.RxErrors, 126 TxError: iface.TxErrors, 127 RxNoBuf: iface.RxNoBuf, 128 RxMiss: iface.RxMiss, 129 Drops: iface.Drops, 130 Punts: iface.Punts, 131 Ip4: iface.IP4, 132 Ip6: iface.IP6, 133 Mpls: iface.Mpls, 134 }, 135 } 136 137 select { 138 case ch <- vppStats: 139 // stats sent 140 case <-ctx.Done(): 141 return ctx.Err() 142 } 143 } 144 return nil 145 } 146 147 func convertInterfaceCombined(c govppapi.InterfaceCounterCombined) *vpp_interfaces.InterfaceStats_CombinedCounter { 148 return &vpp_interfaces.InterfaceStats_CombinedCounter{ 149 Bytes: c.Bytes, 150 Packets: c.Packets, 151 } 152 }