diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e8cbc1f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +# Build the manager binary +FROM --platform=$BUILDPLATFORM golang:1.22 as builder + +ARG GOARCH='' + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum + +COPY hack hack + +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg \ + go mod download + +# Copy the go source +COPY main.go main.go +COPY metrics/ metrics/ +# Needed for version extraction by go build +COPY .git/ .git/ + +ARG TARGETOS +ARG TARGETARCH + +# Build +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg \ + CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH GO111MODULE=on go build -ldflags="-s -w" -a -o prometheus-exporter main.go + +FROM debian:bullseye-slim +WORKDIR / + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +RUN update-ca-certificates + +COPY --from=builder /workspace/prometheus-exporter . + +EXPOSE 8080 +ENTRYPOINT ["/prometheus-exporter"] diff --git a/Makefile b/Makefile index a023d4a..8bd0903 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,18 @@ +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# This is a requirement for 'setup-envtest.sh' in the test target. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec + +all: build + ##@ General # The help target prints out all targets with their descriptions organized @@ -68,3 +83,21 @@ $(GOIMPORTS): $(LOCALBIN) golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. $(GOLANGCI_LINT): $(LOCALBIN) test -s $(LOCALBIN)/golangci-lint || GOBIN=$(LOCALBIN) go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) + +##@ Build + +.PHONY: build +build: fmt vet ## Build binary. + go build -o bin/prometheus-dpdk-exporter main.go + +.PHONY: install +install: + go install . + +.PHONY: run +run: fmt golangci-lint ## Run exporter from your host. + go run ./main.go + +.PHONY: release +release: fmt lint + goreleaser release --snapshot --rm-dist diff --git a/README.md b/README.md index da8a633..7f8cf01 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,16 @@ [![GitHub License](https://img.shields.io/static/v1?label=License&message=Apache-2.0&color=blue)](LICENSE) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://makeapullrequest.com) -Export dpservice statistics to prometheus readable format. +Export Dpservice statistics to Prometheus readable format. ## About this project -*Insert a short description of your project here...* +The `prometheus-dpdk-exporter` is responsible for monitoring and exposing [Dpservice](https://github.com/ironcore-dev/dpservice) statistics from [DPDK telemetry](https://doc.dpdk.org/guides/howto/telemetry.html). When run, `prometheus-dpdk-exporter` creates a simple web server (on a configurable port), on which statistics can be reached. These statistics are updated in configurable time intervals and can be then visualized in dashboard tools like [Grafana](https://grafana.com/). Currently, it provides a solution to get the number of NAT ports used, the number of Virtual services used and other Interface statistics exported as [Prometheus metrics](https://prometheus.io/docs/instrumenting/exposition_formats/). ## Requirements and Setup -*Insert a short description what is required to get your project running...* +[Dpservice](https://github.com/ironcore-dev/dpservice) needs to be running on the same host to run `prometheus-dpdk-exporter` and to export the statistics `prometheus-dpdk-exporter` needs to have access to the socket with the path specified in variable `metrics.SocketPath` *(/var/run/dpdk/rte/dpdk_telemetry.v2)*. +Also specified port (by default 8080) on which we want to run `prometheus-dpdk-exporter` needs to be available. ## Support, Feedback, Contributing diff --git a/go.mod b/go.mod index e90516d..5905d8b 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + github.com/sirupsen/logrus v1.9.3 + golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect google.golang.org/protobuf v1.28.1 // indirect ) diff --git a/go.sum b/go.sum index ea92525..436a9aa 100644 --- a/go.sum +++ b/go.sum @@ -156,6 +156,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -187,11 +188,15 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -321,8 +326,8 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -467,6 +472,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index 08d6b7e..4dc4e81 100644 --- a/main.go +++ b/main.go @@ -4,19 +4,17 @@ package main import ( - "bytes" - "encoding/json" "flag" - "fmt" - "log" "net" "net/http" + "net/netip" "os" - "strings" "time" + "github.com/ironcore-dev/prometheus-dpdk-exporter/metrics" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/sirupsen/logrus" ) const ( @@ -24,171 +22,123 @@ const ( sleepTime = 10 * time.Second ) -var ( - socketPath = "/var/run/dpdk/rte/dpdk_telemetry.v2" - - promMetrics = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "dpdk_interface_stat", - Help: "DPDK interface statistic", - }, - []string{"interface", "stat_name"}, - ) - - promMetricsCallCount = prometheus.NewGaugeVec( - prometheus.GaugeOpts{ - Name: "dpdk_graph_stat", - Help: "Dp-Service graph statistics", - }, - []string{"node_name", "graph_node"}, - ) -) - -type EthdevList struct { - Value []int `json:"/ethdev/list"` -} - -type EthdevInfo struct { - Value struct { - Name string `json:"name"` - } `json:"/ethdev/info"` -} - -type EthdevXstats struct { - Value map[string]float64 `json:"/ethdev/xstats"` -} +func main() { + var conn net.Conn + var err error + var host string + var hostnameFlag string + var pollIntervalFlag int + var exporterPort uint64 + var exporterAddr netip.AddrPort -type DpServiceNatPort struct { - Value map[string]int `json:"/dp_service/nat/used_port_count"` -} + log := logrus.New() + log.Formatter = new(logrus.JSONFormatter) -type NodeData map[string]float64 + r := prometheus.NewRegistry() + r.MustRegister(metrics.InterfaceStat) + r.MustRegister(metrics.CallCount) -type GraphCallCount struct { - Node_0_to_255 NodeData `json:"Node_0_to_255"` -} + http.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{})) -type DpServiceGraphCallCount struct { - GraphCallCnt GraphCallCount `json:"/dp_service/graph/call_count"` -} + conn = connectToDpdkTelemetry(log) + defer conn.Close() -func flushSocket(conn net.Conn) { - respBytes := make([]byte, 1024) + flag.StringVar(&hostnameFlag, "hostname", "", "Hostname to use") + flag.IntVar(&pollIntervalFlag, "poll-interval", 20, "Polling interval in seconds") + flag.Uint64Var(&exporterPort, "port", 8080, "Port on which exporter will be running.") + flag.Parse() - _, err := conn.Read(respBytes) - if err != nil { - log.Fatalf("Failed to read response from %s: %v", socketPath, err) + if exporterPort < 1024 || exporterPort > 65535 { + log.Fatal("port must be in range 1024 - 65535") } -} + exporterAddr = netip.AddrPortFrom(netip.IPv4Unspecified(), uint16(exporterPort)) -func queryTelemetry(conn net.Conn, command string, response interface{}) { - _, err := conn.Write([]byte(command)) + host, err = getHostname(hostnameFlag) if err != nil { - log.Fatalf("Failed to send command to %s: %v", socketPath, err) + log.Fatal("could not get hostname") } + log.Infof("Hostname: %s", host) - respBytes := make([]byte, 1024*6) - var responseBuffer bytes.Buffer - for { - n, err := conn.Read(respBytes) - if err != nil { - log.Fatalf("Failed to read response from %s: %v", socketPath, err) - } - responseBuffer.Write(respBytes[:n]) - parts := strings.SplitN(command, ",", 2) - command = parts[0] - if bytes.Contains(respBytes, []byte(command)) { - break + go func() { + for { + if !testDpdkConnection(conn, log) { + log.Infof("Reconnecting to %s", metrics.SocketPath) + conn = connectToDpdkTelemetry(log) + log.Infof("Reconnected to %s", metrics.SocketPath) + } + metrics.Update(conn, host, log) + time.Sleep(time.Duration(pollIntervalFlag) * time.Second) } - } + }() - err = json.Unmarshal(responseBuffer.Bytes(), response) + log.Infof("Server starting on :%v...", exporterPort) + + err = http.ListenAndServe(exporterAddr.String(), nil) if err != nil { - log.Fatalf("Failed to unmarshal JSON response: %v", err) + log.Fatalf("ListenAndServe failed: %d", err) } } -func updateMetrics(conn net.Conn, hostname string) { - var ethdevList EthdevList - queryTelemetry(conn, "/ethdev/list", ðdevList) - - for _, id := range ethdevList.Value { - var ethdevInfo EthdevInfo - queryTelemetry(conn, fmt.Sprintf("/ethdev/info,%d", id), ðdevInfo) - - var ethdevXstats EthdevXstats - queryTelemetry(conn, fmt.Sprintf("/ethdev/xstats,%d", id), ðdevXstats) - - for statName, statValueFloat := range ethdevXstats.Value { - promMetrics.With(prometheus.Labels{"interface": ethdevInfo.Value.Name, "stat_name": statName}).Set(statValueFloat) - } - } - var dpserviceNatPort DpServiceNatPort - queryTelemetry(conn, "/dp_service/nat/used_port_count", &dpserviceNatPort) - - for ifName, portCount := range dpserviceNatPort.Value { - promMetrics.With(prometheus.Labels{"interface": ifName, "stat_name": "nat_used_port_count"}).Set(float64(portCount)) +// Tests if DPDK telemetry connection is working by writing to the connection +func testDpdkConnection(conn net.Conn, log *logrus.Logger) bool { + _, err := conn.Write([]byte("/")) + if err != nil { + return false } - - var dpserviceCallCount DpServiceGraphCallCount - queryTelemetry(conn, "/dp_service/graph/call_count", &dpserviceCallCount) - - for graphNodeName, callCount := range dpserviceCallCount.GraphCallCnt.Node_0_to_255 { - promMetricsCallCount.With(prometheus.Labels{"node_name": hostname, "graph_node": graphNodeName}).Set(callCount) + flushErr := flushSocket(conn) + if flushErr != nil { + log.Fatalf("Failed to read response from %s: %v", metrics.SocketPath, err) } + return true } -func main() { - var conn net.Conn - var err error - - r := prometheus.NewRegistry() - r.MustRegister(promMetrics) - r.MustRegister(promMetricsCallCount) - - http.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{})) - +// Connects to the DPDK telemetry +func connectToDpdkTelemetry(log *logrus.Logger) net.Conn { for i := 0; i < maxRetries; i++ { - conn, err = net.Dial("unixpacket", socketPath) + conn, err := net.Dial("unixpacket", metrics.SocketPath) if err == nil { - break + err = flushSocket(conn) + if err != nil { + log.Fatalf("Failed to read response from %s: %v", metrics.SocketPath, err) + } + return conn } - log.Printf("Failed to connect to %s: %v. Retry %d of %d", socketPath, err, i+1, maxRetries) + log.Warningf("Failed to connect to %s: %v. Retry %d of %d", metrics.SocketPath, err, i+1, maxRetries) if i < maxRetries-1 { time.Sleep(sleepTime) } + if i == maxRetries-1 { + log.Fatal("Exiting. Maximum connection retries reached") + } } - defer conn.Close() - var host string - hostnameFlag := flag.String("hostname", "", "Hostname to use") - pollIntervalFlag := flag.Int("poll-interval", 20, "Polling interval in seconds") - flag.Parse() + return nil +} + +// Flushes the connection socket +func flushSocket(conn net.Conn) error { + respBytes := make([]byte, 1024) + + _, err := conn.Read(respBytes) + return err +} - if *hostnameFlag == "" { +// Gets the hostname from flag, env variable or OS hostname +func getHostname(hostnameFlag string) (string, error) { + if hostnameFlag == "" { // Try to get hostname from environment variable envHostName := os.Getenv("NODE_NAME") if envHostName != "" { - host = envHostName + return envHostName, nil } else { // If environment variable not set, get hostname from os.Hostname - host, err = os.Hostname() + hostname, err := os.Hostname() if err != nil { - fmt.Printf("Error retrieving hostname: %v\n", err) + return "unknown", err + } else { + return hostname, nil } } } else { - host = *hostnameFlag + return hostnameFlag, nil } - fmt.Printf("Hostname: %s\n", host) - - flushSocket(conn) - go func() { - for { - updateMetrics(conn, host) - time.Sleep(time.Duration(*pollIntervalFlag) * time.Second) - } - }() - - log.Println("Server starting on :9064...") - log.Fatal(http.ListenAndServe(":9064", nil)) } diff --git a/metrics/metrics.go b/metrics/metrics.go new file mode 100644 index 0000000..5da5fad --- /dev/null +++ b/metrics/metrics.go @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package metrics + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "strings" + + "github.com/prometheus/client_golang/prometheus" + "github.com/sirupsen/logrus" +) + +var SocketPath = "/var/run/dpdk/rte/dpdk_telemetry.v2" + +func queryTelemetry(conn net.Conn, log *logrus.Logger, command string, response interface{}) { + _, err := conn.Write([]byte(command)) + if err != nil { + log.Errorf("Failed to send command to %s: %v", SocketPath, err) + return + } + + respBytes := make([]byte, 1024*6) + var responseBuffer bytes.Buffer + for { + n, err := conn.Read(respBytes) + if err != nil { + log.Errorf("Failed to read response from %s: %v", SocketPath, err) + return + } + responseBuffer.Write(respBytes[:n]) + parts := strings.SplitN(command, ",", 2) + command = parts[0] + if bytes.Contains(respBytes, []byte(command)) { + break + } + } + + err = json.Unmarshal(responseBuffer.Bytes(), response) + if err != nil { + log.Errorf("Failed to unmarshal JSON response: %v", err) + } +} + +func Update(conn net.Conn, hostname string, log *logrus.Logger) { + var ethdevList EthdevList + queryTelemetry(conn, log, "/ethdev/list", ðdevList) + + for _, id := range ethdevList.Value { + var ethdevInfo EthdevInfo + queryTelemetry(conn, log, fmt.Sprintf("/ethdev/info,%d", id), ðdevInfo) + + var ethdevXstats EthdevXstats + queryTelemetry(conn, log, fmt.Sprintf("/ethdev/xstats,%d", id), ðdevXstats) + + for statName, statValueFloat := range ethdevXstats.Value { + InterfaceStat.With(prometheus.Labels{"interface": ethdevInfo.Value.Name, "stat_name": statName}).Set(statValueFloat) + } + } + var dpserviceNatPort DpServiceNatPort + queryTelemetry(conn, log, "/dp_service/nat/used_port_count", &dpserviceNatPort) + for ifName, portCount := range dpserviceNatPort.Value { + InterfaceStat.With(prometheus.Labels{"interface": ifName, "stat_name": "nat_used_port_count"}).Set(float64(portCount)) + } + + var dpserviceVirtsvcPort DpServiceVirtsvcPort + queryTelemetry(conn, log, "/dp_service/virtsvc/used_port_count", &dpserviceVirtsvcPort) + for ifName, portCount := range dpserviceVirtsvcPort.Value { + InterfaceStat.With(prometheus.Labels{"interface": ifName, "stat_name": "virtsvc_used_port_count"}).Set(float64(portCount)) + } + + var dpserviceCallCount DpServiceGraphCallCount + queryTelemetry(conn, log, "/dp_service/graph/call_count", &dpserviceCallCount) + + for graphNodeName, callCount := range dpserviceCallCount.GraphCallCnt.Node_0_to_255 { + CallCount.With(prometheus.Labels{"node_name": hostname, "graph_node": graphNodeName}).Set(callCount) + } +} diff --git a/metrics/types.go b/metrics/types.go new file mode 100644 index 0000000..cce277b --- /dev/null +++ b/metrics/types.go @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package metrics + +import "github.com/prometheus/client_golang/prometheus" + +var ( + InterfaceStat = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "dpdk_interface_stat", + Help: "DPDK interface statistic", + }, + []string{"interface", "stat_name"}, + ) + + CallCount = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Name: "dpdk_graph_stat", + Help: "Dp-Service graph statistics", + }, + []string{"node_name", "graph_node"}, + ) +) + +type EthdevList struct { + Value []int `json:"/ethdev/list"` +} + +type EthdevInfo struct { + Value struct { + Name string `json:"name"` + } `json:"/ethdev/info"` +} + +type EthdevXstats struct { + Value map[string]float64 `json:"/ethdev/xstats"` +} + +type DpServiceNatPort struct { + Value map[string]int `json:"/dp_service/nat/used_port_count"` +} + +type DpServiceVirtsvcPort struct { + Value map[string]int `json:"/dp_service/virtsvc/used_port_count"` +} + +type NodeData map[string]float64 + +type GraphCallCount struct { + Node_0_to_255 NodeData `json:"Node_0_to_255"` +} + +type DpServiceGraphCallCount struct { + GraphCallCnt GraphCallCount `json:"/dp_service/graph/call_count"` +}