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 }