From bdb6a98a58f3bc98cc8c8ceb9529e844bf244e90 Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Thu, 22 Oct 2020 12:41:19 +0200 Subject: [PATCH] feat: add reading VPP threads to the telemetry plugin * Added 'GetThreads' for all supported VPP versions in the telemetry vppcalls. Data are read using the binary API (via govppmux) * VPP stats API telemetry handler also has CLI/binapi part in order to read data not yet available in stats * Added simple stats example reading VPP stats via the telemetry plugin TODO: * Fix 'GetMemory' and add it to the example Signed-off-by: Vladimir Lavor --- examples/vpp_stats/main.go | 215 ++++++++++++++++++ .../govppmux/vppcalls/vpp1908/vpe_vppcalls.go | 20 ++ .../govppmux/vppcalls/vpp2001/vpe_vppcalls.go | 20 ++ .../govppmux/vppcalls/vpp2005/vpe_vppcalls.go | 20 ++ .../govppmux/vppcalls/vpp2009/vpe_vppcalls.go | 20 ++ plugins/govppmux/vppcalls/vpp_handler_api.go | 15 +- .../vppcalls/telemetry_handler_api.go | 68 ++++-- plugins/telemetry/vppcalls/telemetry_stats.go | 26 ++- .../vppcalls/vpp1908/telemetry_vppcalls.go | 23 ++ .../vppcalls/vpp2001/telemetry_vppcalls.go | 23 ++ .../vppcalls/vpp2005/telemetry_vppcalls.go | 23 ++ .../vppcalls/vpp2009/telemetry_vppcalls.go | 24 ++ 12 files changed, 480 insertions(+), 17 deletions(-) create mode 100644 examples/vpp_stats/main.go diff --git a/examples/vpp_stats/main.go b/examples/vpp_stats/main.go new file mode 100644 index 0000000000..f982410c76 --- /dev/null +++ b/examples/vpp_stats/main.go @@ -0,0 +1,215 @@ +// Copyright (c) 2020 Cisco and/or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The VPP stats example shows how to use telemetry API to access +// VPP stats via the GoVPP stats socket API and the telemetry vpp calls. + +package main + +import ( + "context" + "fmt" + "git.fd.io/govpp.git/api" + "go.ligato.io/cn-infra/v2/agent" + "go.ligato.io/cn-infra/v2/config" + "go.ligato.io/cn-infra/v2/logging" + "go.ligato.io/cn-infra/v2/logging/logrus" + "go.ligato.io/vpp-agent/v3/plugins/govppmux" + "go.ligato.io/vpp-agent/v3/plugins/telemetry" + "go.ligato.io/vpp-agent/v3/plugins/telemetry/vppcalls" + "log" + "time" +) + +const PluginName = "stats-example" + +func main() { + ep := &StatsExamplePlugin{ + Log: logging.DefaultLogger, + Telemetry: &telemetry.DefaultPlugin, + } + stopExample := make(chan struct{}) + + a := agent.NewAgent( + agent.AllPlugins(ep), + agent.QuitOnClose(stopExample), + ) + if err := a.Run(); err != nil { + log.Fatal(err) + } + + go closeExample("Stats example finished", stopExample) +} + +// StatsExamplePlugin displays VPP stats using telemetry plugin +type StatsExamplePlugin struct { + handler vppcalls.TelemetryVppAPI + + config.PluginConfig + Log logging.Logger + Telemetry *telemetry.Plugin +} + +func (p *StatsExamplePlugin) Init() error { + var err error + p.handler, err = vppcalls.NewHandler(&govppmux.DefaultPlugin) + if err != nil { + panic(err) + } + + go p.processStats() + return nil +} + +func (p *StatsExamplePlugin) Close() error { + p.Log.Info("Stats example closed") + return nil +} + +func (p *StatsExamplePlugin) String() string { + return PluginName +} + +func closeExample(message string, stopExample chan struct{}) { + time.Sleep(10 * time.Second) + logrus.DefaultLogger().Info(message) + close(stopExample) +} + +func (p *StatsExamplePlugin) processStats() { + // give the Agent some time to initialize + // so the output is not mixed + time.Sleep(1 * time.Second) + p.Log.Infoln("Processing stats") + + var errors []error + + // collect stats + ifStats, err := p.handler.GetInterfaceStats(context.Background()) + if err != nil { + errors = append(errors, fmt.Errorf("eroror retireving interface stats: %v", err)) + } + nodeCounters, err := p.handler.GetNodeCounters(context.Background()) + if err != nil { + errors = append(errors, fmt.Errorf("eroror retireving node counters: %v", err)) + } + + systemStats, err := p.handler.GetSystemStats(context.Background()) + if err != nil { + errors = append(errors, fmt.Errorf("eroror retireving system stats: %v", err)) + } + + runtimeInfo, err := p.handler.GetRuntimeInfo(context.Background()) + if err != nil { + errors = append(errors, fmt.Errorf("eroror retireving runtime info: %v", err)) + } + + bufferInfo, err := p.handler.GetBuffersInfo(context.Background()) + if err != nil { + errors = append(errors, fmt.Errorf("eroror retireving buffers info: %v", err)) + } + + threadsInfo, err := p.handler.GetThreads(context.Background()) + if err != nil { + errors = append(errors, fmt.Errorf("eroror retireving threads: %v", err)) + } + + // print all errors and return if there is any + if len(errors) != 0 { + for _, err := range errors { + p.Log.Error(err) + } + return + } + + // print collected stats + printIfStats(ifStats) + printNodeCounters(nodeCounters) + printSystemStats(systemStats) + printRuntimeInfo(runtimeInfo) + printBufferInfo(bufferInfo) + printThreadsInfo(threadsInfo) +} + +func printIfStats(ifStats *api.InterfaceStats) { + for _, ifStat := range ifStats.Interfaces { + fmt.Printf(` +Interface name: %s (sw_if_idx %d) + Received: %d (rx errors %d) + Transmitted: %d (tx errors %d) + Drops: %d +`, ifStat.InterfaceName, ifStat.InterfaceIndex, ifStat.Rx, ifStat.RxErrors, + ifStat.Tx, ifStat.TxErrors, ifStat.Drops) + } +} + +func printNodeCounters(nodeCountersInfo *vppcalls.NodeCounterInfo) { + maxLen := 5 + for i, nodeCounters := range nodeCountersInfo.GetCounters() { + if i >= maxLen { + // do not print everything, it is not necessary + break + } + fmt.Printf(` +Node name: %s +Node: %s + +`, nodeCounters.Name, nodeCounters.Node) + } + if len(nodeCountersInfo.GetCounters()) >= maxLen { + fmt.Printf("... and another %d nodes\n", len(nodeCountersInfo.GetCounters())-maxLen) + } +} + +func printSystemStats(systemStats *api.SystemStats) { + fmt.Printf(` +Last update: %d +Last stats clear: %d +Input rate: %d +Num. Worker Threads: %d +Vector rate: %d (per worker: %+v) +Heartbeat: %d +`, systemStats.LastUpdate, systemStats.LastStatsClear, systemStats.InputRate, systemStats.NumWorkerThreads, + systemStats.VectorRate, systemStats.VectorRatePerWorker, systemStats.Heartbeat) +} + +func printRuntimeInfo(runtimeInfo *vppcalls.RuntimeInfo) { + for _, thread := range runtimeInfo.GetThreads() { + fmt.Printf("\nThread: %s (ID %d)", thread.Name, thread.ID) + } +} + +func printBufferInfo(bufferInfo *vppcalls.BuffersInfo) { + for _, buffer := range bufferInfo.GetItems() { + fmt.Printf(` + +Buffer name: %s (index %d) + Alloc: %d (num %d) + Free: %d (num %d) + Size: %d + Thread ID: %d +`, buffer.Name, buffer.Index, buffer.Alloc, buffer.NumAlloc, buffer.Free, buffer.NumFree, buffer.Size, buffer.ThreadID) + } +} + +func printThreadsInfo(threadsInfo *vppcalls.ThreadsInfo) { + for _, thread := range threadsInfo.GetItems() { + fmt.Printf(` +Thread name: %s (ID %d) + Type: %s + PID: %d + Core: %d (CPU ID %d, CPU socket %d) +`, thread.Name, thread.ID, thread.Type, thread.PID, thread.Core, thread.CPUID, thread.CPUSocket) + } +} diff --git a/plugins/govppmux/vppcalls/vpp1908/vpe_vppcalls.go b/plugins/govppmux/vppcalls/vpp1908/vpe_vppcalls.go index 39ff3dbcfb..c8d0d32c33 100644 --- a/plugins/govppmux/vppcalls/vpp1908/vpe_vppcalls.go +++ b/plugins/govppmux/vppcalls/vpp1908/vpe_vppcalls.go @@ -132,6 +132,26 @@ func (h *VpeHandler) GetPlugins(ctx context.Context) ([]vppcalls.PluginInfo, err return plugins, nil } +func (h *VpeHandler) GetThreads(ctx context.Context) ([]vppcalls.ThreadInfo, error) { + resp, err := h.vpe.ShowThreads(ctx, &vpe.ShowThreads{}) + if err != nil { + return nil, err + } + threads := make([]vppcalls.ThreadInfo, len(resp.ThreadData)) + for i, thread := range resp.ThreadData { + threads[i] = vppcalls.ThreadInfo{ + Name: string(thread.Name), + ID: thread.ID, + Type: string(thread.Type), + PID: thread.PID, + Core: thread.Core, + CPUID: thread.CPUID, + CPUSocket: thread.CPUSocket, + } + } + return threads, nil +} + // RunCli sends CLI command to VPP and returns response. func (h *VpeHandler) RunCli(ctx context.Context, cmd string) (string, error) { resp, err := h.vpe.CliInband(ctx, &vpe.CliInband{ diff --git a/plugins/govppmux/vppcalls/vpp2001/vpe_vppcalls.go b/plugins/govppmux/vppcalls/vpp2001/vpe_vppcalls.go index f1202a8058..38fd18794e 100644 --- a/plugins/govppmux/vppcalls/vpp2001/vpe_vppcalls.go +++ b/plugins/govppmux/vppcalls/vpp2001/vpe_vppcalls.go @@ -134,6 +134,26 @@ func (h *VpeHandler) GetPlugins(ctx context.Context) ([]vppcalls.PluginInfo, err return plugins, nil } +func (h *VpeHandler) GetThreads(ctx context.Context) ([]vppcalls.ThreadInfo, error) { + resp, err := h.vpe.ShowThreads(ctx, &vpe.ShowThreads{}) + if err != nil { + return nil, err + } + threads := make([]vppcalls.ThreadInfo, len(resp.ThreadData)) + for i, thread := range resp.ThreadData { + threads[i] = vppcalls.ThreadInfo{ + Name: string(thread.Name), + ID: thread.ID, + Type: string(thread.Type), + PID: thread.PID, + Core: thread.Core, + CPUID: thread.CPUID, + CPUSocket: thread.CPUSocket, + } + } + return threads, nil +} + // RunCli sends CLI command to VPP and returns response. func (h *VpeHandler) RunCli(ctx context.Context, cmd string) (string, error) { resp, err := h.vpe.CliInband(ctx, &vpe.CliInband{ diff --git a/plugins/govppmux/vppcalls/vpp2005/vpe_vppcalls.go b/plugins/govppmux/vppcalls/vpp2005/vpe_vppcalls.go index abeb8567d7..f79131c79d 100644 --- a/plugins/govppmux/vppcalls/vpp2005/vpe_vppcalls.go +++ b/plugins/govppmux/vppcalls/vpp2005/vpe_vppcalls.go @@ -134,6 +134,26 @@ func (h *VpeHandler) GetPlugins(ctx context.Context) ([]vppcalls.PluginInfo, err return plugins, nil } +func (h *VpeHandler) GetThreads(ctx context.Context) ([]vppcalls.ThreadInfo, error) { + resp, err := h.vpe.ShowThreads(ctx, &vpe.ShowThreads{}) + if err != nil { + return nil, err + } + threads := make([]vppcalls.ThreadInfo, len(resp.ThreadData)) + for i, thread := range resp.ThreadData { + threads[i] = vppcalls.ThreadInfo{ + Name: thread.Name, + ID: thread.ID, + Type: thread.Type, + PID: thread.PID, + Core: thread.Core, + CPUID: thread.CPUID, + CPUSocket: thread.CPUSocket, + } + } + return threads, nil +} + // RunCli sends CLI command to VPP and returns response. func (h *VpeHandler) RunCli(ctx context.Context, cmd string) (string, error) { resp, err := h.vpe.CliInband(ctx, &vpe.CliInband{ diff --git a/plugins/govppmux/vppcalls/vpp2009/vpe_vppcalls.go b/plugins/govppmux/vppcalls/vpp2009/vpe_vppcalls.go index 2ac9c9ae1b..0ccee86001 100644 --- a/plugins/govppmux/vppcalls/vpp2009/vpe_vppcalls.go +++ b/plugins/govppmux/vppcalls/vpp2009/vpe_vppcalls.go @@ -135,6 +135,26 @@ func (h *VpeHandler) GetPlugins(ctx context.Context) ([]vppcalls.PluginInfo, err return plugins, nil } +func (h *VpeHandler) GetThreads(ctx context.Context) ([]vppcalls.ThreadInfo, error) { + resp, err := h.vpe.ShowThreads(ctx, &vpe.ShowThreads{}) + if err != nil { + return nil, err + } + threads := make([]vppcalls.ThreadInfo, len(resp.ThreadData)) + for i, thread := range resp.ThreadData { + threads[i] = vppcalls.ThreadInfo{ + Name: thread.Name, + ID: thread.ID, + Type: thread.Type, + PID: thread.PID, + Core: thread.Core, + CPUID: thread.CPUID, + CPUSocket: thread.CPUSocket, + } + } + return threads, nil +} + // RunCli sends CLI command to VPP and returns response. func (h *VpeHandler) RunCli(ctx context.Context, cmd string) (string, error) { reply, err := h.vpe.CliInband(ctx, &vpe.CliInband{ diff --git a/plugins/govppmux/vppcalls/vpp_handler_api.go b/plugins/govppmux/vppcalls/vpp_handler_api.go index 39a6eafe66..b49c8edcdb 100644 --- a/plugins/govppmux/vppcalls/vpp_handler_api.go +++ b/plugins/govppmux/vppcalls/vpp_handler_api.go @@ -28,7 +28,7 @@ import ( type VppCoreAPI interface { // Ping sends control ping to VPP. Ping(context.Context) error - // RunCli sends CLI commmand to VPP. + // RunCli sends CLI command to VPP. RunCli(ctx context.Context, cmd string) (string, error) // GetVersion retrieves info about VPP version. GetVersion(context.Context) (*VersionInfo, error) @@ -38,6 +38,8 @@ type VppCoreAPI interface { GetModules(context.Context) ([]APIModule, error) // GetPlugins retrieves info about loaded VPP plugins. GetPlugins(context.Context) ([]PluginInfo, error) + // GetThreads retrieves info about VPP threads. + GetThreads(ctx context.Context) ([]ThreadInfo, error) } // SessionInfo contains info about VPP session. @@ -83,6 +85,17 @@ type PluginInfo struct { Description string } +// ThreadInfo wraps all thread data counters. +type ThreadInfo struct { + Name string + ID uint32 + Type string + PID uint32 + CPUID uint32 + Core uint32 + CPUSocket uint32 +} + func (p PluginInfo) String() string { return fmt.Sprintf("%s - %s", p.Name, p.Description) } diff --git a/plugins/telemetry/vppcalls/telemetry_handler_api.go b/plugins/telemetry/vppcalls/telemetry_handler_api.go index ce16cb0bec..6538facc42 100644 --- a/plugins/telemetry/vppcalls/telemetry_handler_api.go +++ b/plugins/telemetry/vppcalls/telemetry_handler_api.go @@ -24,7 +24,7 @@ import ( ) var ( - // FallbackToCli defines wether should telemetry handler + // FallbackToCli defines whether should telemetry handler // fallback to parsing stats from CLI output. FallbackToCli = false ) @@ -37,6 +37,7 @@ type TelemetryVppAPI interface { GetRuntimeInfo(context.Context) (*RuntimeInfo, error) GetBuffersInfo(context.Context) (*BuffersInfo, error) GetInterfaceStats(context.Context) (*govppapi.InterfaceStats, error) + GetThreads(ctx context.Context) (*ThreadsInfo, error) } // MemoryInfo contains memory thread info. @@ -153,6 +154,30 @@ type BuffersItem struct { NumFree uint64 `json:"num_free"` } +// ThreadsInfo contains values returned form `show threads` +type ThreadsInfo struct { + Items []ThreadsItem +} + +// GetItems is safe getter for thread items +func (i *ThreadsInfo) GetItems() []ThreadsItem { + if i == nil { + return nil + } + return i.Items +} + +// ThreadsItem represents single threads item +type ThreadsItem struct { + Name string `json:"name"` + ID uint32 `json:"id"` + Type string `json:"type"` + PID uint32 `json:"pid"` + CPUID uint32 `json:"cpuid"` + Core uint32 `json:"core"` + CPUSocket uint32 `json:"cpu_socket"` +} + var Handler = vpp.RegisterHandler(vpp.HandlerDesc{ Name: "telemetry", HandlerAPI: (*TelemetryVppAPI)(nil), @@ -173,30 +198,47 @@ func AddHandlerVersion(version vpp.Version, msgs []govppapi.Message, h NewHandle }) } +// NewHandler returns the telemetry handler preferring the VPP stats API +// with CLI/binary API handler injected to retrieve data not included in +// stats. In case the stats API is not available, CLI handler is returned. func NewHandler(c vpp.Client) (TelemetryVppAPI, error) { - // Prefer using VPP stats API. + var compatibleHandler TelemetryVppAPI = nil + v, err := Handler.GetCompatibleVersion(c) + if err != nil { + log.Warnf("compatible handler unavailable: %v", err) + } else { + compatibleHandler = v.NewHandler(c).(TelemetryVppAPI) + } + // Prefer the VPP stats API (even without the handler) if stats := c.Stats(); stats != nil { - return NewTelemetryVppStats(stats), nil + return NewTelemetryVppStats(stats, compatibleHandler), nil } - v, err := Handler.GetCompatibleVersion(c) if err != nil { return nil, err } - return v.New.(NewHandlerFunc)(c), nil + return compatibleHandler, nil } +// CompatibleTelemetryHandler returns the telemetry handler respecting +// VPP version. It returns the stats API handler when available, or +// fallbacks to the CLI when requested. func CompatibleTelemetryHandler(c vpp.Client) TelemetryVppAPI { - // Prefer using VPP stats API. - if stats := c.Stats(); stats != nil { - return NewTelemetryVppStats(stats) + var compatibleHandler TelemetryVppAPI = nil + v := Handler.FindCompatibleVersion(c) + if v != nil { + compatibleHandler = v.NewHandler(c).(TelemetryVppAPI) } - if FallbackToCli { - if v := Handler.FindCompatibleVersion(c); v != nil { - log.Info("falling back to parsing CLI output for telemetry") - return v.NewHandler(c).(TelemetryVppAPI) + if FallbackToCli && v != nil { + log.Info("falling back to parsing CLI output for telemetry") + return v.NewHandler(c).(TelemetryVppAPI) + } + if stats := c.Stats(); stats != nil { + if v == nil { + log.Warn("handler unavailable, functionality limited") } - // no compatible version found + return NewTelemetryVppStats(stats, compatibleHandler) } + // no compatible version found log.Warnf("stats connection not available for telemetry") return nil } diff --git a/plugins/telemetry/vppcalls/telemetry_stats.go b/plugins/telemetry/vppcalls/telemetry_stats.go index 350e5b6a89..24fafe51cf 100644 --- a/plugins/telemetry/vppcalls/telemetry_stats.go +++ b/plugins/telemetry/vppcalls/telemetry_stats.go @@ -16,6 +16,7 @@ package vppcalls import ( "context" + "fmt" "regexp" "strings" @@ -26,6 +27,10 @@ import ( // VPP stats API to retrieve the telemetry data. type TelemetryStats struct { stats govppapi.StatsProvider + // telemetry API helps with reading Memory/Threads data + // (i.e. those who are not a part of the stats API or are not + // implemented yet) + telemetryAPI TelemetryVppAPI sysStats govppapi.SystemStats ifStats govppapi.InterfaceStats @@ -34,9 +39,10 @@ type TelemetryStats struct { bufStats govppapi.BufferStats } -func NewTelemetryVppStats(stats govppapi.StatsProvider) *TelemetryStats { +func NewTelemetryVppStats(stats govppapi.StatsProvider, teleApi TelemetryVppAPI) *TelemetryStats { return &TelemetryStats{ - stats: stats, + stats: stats, + telemetryAPI: teleApi, } } @@ -50,11 +56,25 @@ func (h *TelemetryStats) GetSystemStats(context.Context) (*govppapi.SystemStats, } // GetMemory retrieves `show memory` info. +// todo switch to stats when memory data will be implemented func (h *TelemetryStats) GetMemory(ctx context.Context) (*MemoryInfo, error) { - // TODO: retrieve memory stats + if h.telemetryAPI == nil { + return nil, fmt.Errorf("`GetMemory` unavailable, telemetry handler was not provided") + } + // todo failing, temporary disabled + // return h.telemetryAPI.GetMemory(ctx) return nil, nil } +// GetThreads retrieves `show threads` info. +// todo switch to stats when threads data will be available +func (h *TelemetryStats) GetThreads(ctx context.Context) (*ThreadsInfo, error) { + if h.telemetryAPI == nil { + return nil, fmt.Errorf("`GetThreads` unavailable, telemetry handler was not provided") + } + return h.telemetryAPI.GetThreads(ctx) +} + // GetInterfaceStats retrieves interface stats. func (h *TelemetryStats) GetInterfaceStats(context.Context) (*govppapi.InterfaceStats, error) { err := h.stats.GetInterfaceStats(&h.ifStats) diff --git a/plugins/telemetry/vppcalls/vpp1908/telemetry_vppcalls.go b/plugins/telemetry/vppcalls/vpp1908/telemetry_vppcalls.go index 5ea723ef87..36860c67df 100644 --- a/plugins/telemetry/vppcalls/vpp1908/telemetry_vppcalls.go +++ b/plugins/telemetry/vppcalls/vpp1908/telemetry_vppcalls.go @@ -266,6 +266,29 @@ func (h *TelemetryHandler) GetBuffersInfo(ctx context.Context) (*vppcalls.Buffer return info, nil } +// GetThreads retrieves info about the VPP threads +func (h *TelemetryHandler) GetThreads(ctx context.Context) (*vppcalls.ThreadsInfo, error) { + threads, err := h.vpe.GetThreads(ctx) + if err != nil { + return nil, err + } + var items []vppcalls.ThreadsItem + for _, thread := range threads { + items = append(items, vppcalls.ThreadsItem{ + Name: thread.Name, + ID: thread.ID, + Type: thread.Type, + PID: thread.PID, + CPUID: thread.CPUID, + Core: thread.Core, + CPUSocket: thread.CPUSocket, + }) + } + return &vppcalls.ThreadsInfo{ + Items: items, + }, err +} + func strToFloat64(s string) float64 { // Replace 'k' (thousands) with 'e3' to make it parsable with strconv s = strings.Replace(s, "k", "e3", 1) diff --git a/plugins/telemetry/vppcalls/vpp2001/telemetry_vppcalls.go b/plugins/telemetry/vppcalls/vpp2001/telemetry_vppcalls.go index 468538c3d9..6ff1ce80fd 100644 --- a/plugins/telemetry/vppcalls/vpp2001/telemetry_vppcalls.go +++ b/plugins/telemetry/vppcalls/vpp2001/telemetry_vppcalls.go @@ -266,6 +266,29 @@ func (h *TelemetryHandler) GetBuffersInfo(ctx context.Context) (*vppcalls.Buffer return info, nil } +// GetThreads retrieves info about the VPP threads +func (h *TelemetryHandler) GetThreads(ctx context.Context) (*vppcalls.ThreadsInfo, error) { + threads, err := h.vpe.GetThreads(ctx) + if err != nil { + return nil, err + } + var items []vppcalls.ThreadsItem + for _, thread := range threads { + items = append(items, vppcalls.ThreadsItem{ + Name: thread.Name, + ID: thread.ID, + Type: thread.Type, + PID: thread.PID, + CPUID: thread.CPUID, + Core: thread.Core, + CPUSocket: thread.CPUSocket, + }) + } + return &vppcalls.ThreadsInfo{ + Items: items, + }, err +} + func strToFloat64(s string) float64 { // Replace 'k' (thousands) with 'e3' to make it parsable with strconv s = strings.Replace(s, "k", "e3", 1) diff --git a/plugins/telemetry/vppcalls/vpp2005/telemetry_vppcalls.go b/plugins/telemetry/vppcalls/vpp2005/telemetry_vppcalls.go index 890513c908..b51734756c 100644 --- a/plugins/telemetry/vppcalls/vpp2005/telemetry_vppcalls.go +++ b/plugins/telemetry/vppcalls/vpp2005/telemetry_vppcalls.go @@ -266,6 +266,29 @@ func (h *TelemetryHandler) GetBuffersInfo(ctx context.Context) (*vppcalls.Buffer return info, nil } +// GetThreads retrieves info about the VPP threads +func (h *TelemetryHandler) GetThreads(ctx context.Context) (*vppcalls.ThreadsInfo, error) { + threads, err := h.vpe.GetThreads(ctx) + if err != nil { + return nil, err + } + var items []vppcalls.ThreadsItem + for _, thread := range threads { + items = append(items, vppcalls.ThreadsItem{ + Name: thread.Name, + ID: thread.ID, + Type: thread.Type, + PID: thread.PID, + CPUID: thread.CPUID, + Core: thread.Core, + CPUSocket: thread.CPUSocket, + }) + } + return &vppcalls.ThreadsInfo{ + Items: items, + }, err +} + func strToFloat64(s string) float64 { // Replace 'k' (thousands) with 'e3' to make it parsable with strconv s = strings.Replace(s, "k", "e3", 1) diff --git a/plugins/telemetry/vppcalls/vpp2009/telemetry_vppcalls.go b/plugins/telemetry/vppcalls/vpp2009/telemetry_vppcalls.go index d5a71bd83b..5969cc323e 100644 --- a/plugins/telemetry/vppcalls/vpp2009/telemetry_vppcalls.go +++ b/plugins/telemetry/vppcalls/vpp2009/telemetry_vppcalls.go @@ -266,6 +266,30 @@ func (h *TelemetryHandler) GetBuffersInfo(ctx context.Context) (*vppcalls.Buffer return info, nil } +// GetThreads retrieves info about the VPP threads +func (h *TelemetryHandler) GetThreads(ctx context.Context) (*vppcalls.ThreadsInfo, error) { + threads, err := h.vpe.GetThreads(ctx) + if err != nil { + return nil, err + } + var items []vppcalls.ThreadsItem + for _, thread := range threads { + fmt.Printf("thread: %v", thread) + items = append(items, vppcalls.ThreadsItem{ + Name: thread.Name, + ID: thread.ID, + Type: thread.Type, + PID: thread.PID, + CPUID: thread.CPUID, + Core: thread.Core, + CPUSocket: thread.CPUSocket, + }) + } + return &vppcalls.ThreadsInfo{ + Items: items, + }, err +} + func strToFloat64(s string) float64 { // Replace 'k' (thousands) with 'e3' to make it parsable with strconv s = strings.Replace(s, "k", "e3", 1)