go.ligato.io/vpp-agent/v3@v3.5.0/plugins/telemetry/vppcalls/telemetry_stats.go (about)

     1  //  Copyright (c) 2019 Cisco and/or its affiliates.
     2  //
     3  //  Licensed under the Apache License, Version 2.0 (the "License");
     4  //  you may not use this file except in compliance with the License.
     5  //  You may obtain a copy of the License at:
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  //  Unless required by applicable law or agreed to in writing, software
    10  //  distributed under the License is distributed on an "AS IS" BASIS,
    11  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  //  See the License for the specific language governing permissions and
    13  //  limitations under the License.
    14  
    15  package vppcalls
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"regexp"
    21  	"strings"
    22  
    23  	govppapi "go.fd.io/govpp/api"
    24  )
    25  
    26  // TelemetryStats is an implementation of TelemetryVppAPI that uses
    27  // VPP stats API to retrieve the telemetry data.
    28  type TelemetryStats struct {
    29  	stats govppapi.StatsProvider
    30  	// telemetry API helps with reading Memory/Threads data
    31  	// (i.e. those who are not a part of the stats API or are not
    32  	// implemented yet)
    33  	telemetryAPI TelemetryVppAPI
    34  
    35  	sysStats  govppapi.SystemStats
    36  	ifStats   govppapi.InterfaceStats
    37  	nodeStats govppapi.NodeStats
    38  	errStats  govppapi.ErrorStats
    39  	bufStats  govppapi.BufferStats
    40  }
    41  
    42  func NewTelemetryVppStats(stats govppapi.StatsProvider, teleApi TelemetryVppAPI) *TelemetryStats {
    43  	return &TelemetryStats{
    44  		stats:        stats,
    45  		telemetryAPI: teleApi,
    46  	}
    47  }
    48  
    49  // GetSystemStats retrieves system stats.
    50  func (h *TelemetryStats) GetSystemStats(context.Context) (*govppapi.SystemStats, error) {
    51  	err := h.stats.GetSystemStats(&h.sysStats)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	return &h.sysStats, nil
    56  }
    57  
    58  // GetMemory retrieves `show memory` info.
    59  // todo switch to stats when memory data will be implemented
    60  func (h *TelemetryStats) GetMemory(ctx context.Context) (*MemoryInfo, error) {
    61  	if h.telemetryAPI == nil {
    62  		return nil, fmt.Errorf("`GetMemory` unavailable, telemetry handler was not provided")
    63  	}
    64  	return h.telemetryAPI.GetMemory(ctx)
    65  }
    66  
    67  // GetThreads retrieves `show threads` info.
    68  // todo switch to stats when threads data will be available
    69  func (h *TelemetryStats) GetThreads(ctx context.Context) (*ThreadsInfo, error) {
    70  	if h.telemetryAPI == nil {
    71  		return nil, fmt.Errorf("`GetThreads` unavailable, telemetry handler was not provided")
    72  	}
    73  	return h.telemetryAPI.GetThreads(ctx)
    74  }
    75  
    76  // GetInterfaceStats retrieves interface stats.
    77  func (h *TelemetryStats) GetInterfaceStats(context.Context) (*govppapi.InterfaceStats, error) {
    78  	err := h.stats.GetInterfaceStats(&h.ifStats)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	return &h.ifStats, nil
    83  }
    84  
    85  // GetNodeCounters retrieves node counters info.
    86  func (h *TelemetryStats) GetNodeCounters(ctx context.Context) (*NodeCounterInfo, error) {
    87  	err := h.stats.GetErrorStats(&h.errStats)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	var counters []NodeCounter
    93  	for _, c := range h.errStats.Errors {
    94  		node, reason := SplitErrorName(c.CounterName)
    95  		var valSum uint64 = 0
    96  		// c.Values are per worker counters
    97  		for _, val := range c.Values {
    98  			valSum += val
    99  		}
   100  		counters = append(counters, NodeCounter{
   101  			Value: valSum,
   102  			Node:  node,
   103  			Name:  reason,
   104  		})
   105  	}
   106  
   107  	info := &NodeCounterInfo{
   108  		Counters: counters,
   109  	}
   110  
   111  	return info, nil
   112  }
   113  
   114  // GetRuntimeInfo retrieves how runtime info.
   115  func (h *TelemetryStats) GetRuntimeInfo(ctx context.Context) (*RuntimeInfo, error) {
   116  	err := h.stats.GetNodeStats(&h.nodeStats)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	thread := RuntimeThread{
   122  		Name: "ALL",
   123  	}
   124  	for _, node := range h.nodeStats.Nodes {
   125  		vpc := 0.0
   126  		if node.Vectors != 0 && node.Calls != 0 {
   127  			vpc = float64(node.Vectors) / float64(node.Calls)
   128  		}
   129  		thread.Items = append(thread.Items, RuntimeItem{
   130  			Index:          uint(node.NodeIndex),
   131  			Name:           node.NodeName,
   132  			Calls:          node.Calls,
   133  			Vectors:        node.Vectors,
   134  			Suspends:       node.Suspends,
   135  			Clocks:         float64(node.Clocks),
   136  			VectorsPerCall: vpc,
   137  		})
   138  	}
   139  
   140  	threads := []RuntimeThread{
   141  		thread,
   142  	}
   143  
   144  	return &RuntimeInfo{
   145  		Threads: threads,
   146  	}, nil
   147  }
   148  
   149  // GetBuffersInfo retrieves buffers info from VPP.
   150  func (h *TelemetryStats) GetBuffersInfo(ctx context.Context) (*BuffersInfo, error) {
   151  	err := h.stats.GetBufferStats(&h.bufStats)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	var items []BuffersItem
   157  
   158  	for _, c := range h.bufStats.Buffer {
   159  		items = append(items, BuffersItem{
   160  			Name:  c.PoolName,
   161  			Alloc: uint64(c.Used),
   162  			Free:  uint64(c.Available),
   163  			// Cached:  c.Cached,
   164  		})
   165  	}
   166  
   167  	info := &BuffersInfo{
   168  		Items: items,
   169  	}
   170  
   171  	return info, nil
   172  }
   173  
   174  var (
   175  	errorNameLikeMemifRe   = regexp.MustCompile(`^[A-Za-z0-9-]+([0-9]+\/[0-9]+|pg\/stream)`)
   176  	errorNameLikeGigabitRe = regexp.MustCompile(`^[A-Za-z0-9]+[0-9a-f]+(\/[0-9a-f]+){2}`)
   177  )
   178  
   179  func SplitErrorName(str string) (node, reason string) {
   180  	parts := strings.Split(str, "/")
   181  	switch len(parts) {
   182  	case 1:
   183  		return parts[0], ""
   184  	case 2:
   185  		return parts[0], parts[1]
   186  	case 3:
   187  		if strings.Contains(parts[1], " ") {
   188  			return parts[0], strings.Join(parts[1:], "/")
   189  		}
   190  		if errorNameLikeMemifRe.MatchString(str) {
   191  			return strings.Join(parts[:2], "/"), parts[2]
   192  		}
   193  	default:
   194  		if strings.Contains(parts[2], " ") {
   195  			return strings.Join(parts[:2], "/"), strings.Join(parts[2:], "/")
   196  		}
   197  		if errorNameLikeGigabitRe.MatchString(str) {
   198  			return strings.Join(parts[:3], "/"), strings.Join(parts[3:], "/")
   199  		}
   200  	}
   201  	return strings.Join(parts[:len(parts)-1], "/"), parts[len(parts)-1]
   202  }