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  }