Node interface metrics application server code
This commit adds code in GO language to expose Physical Function interface device info and statistics (metrics) of a node using REST API service. NIC Statistics provided by Netlink. Following APIs are implemented: /metrics -- all statistics in OpenMetrics format /metrics/device/{DeviceName} -- particular device statistics in OpenMetrics format /metrics/pci-addr/{PciAddr} -- particular pci-address statistics in OpenMetrics format /json/metrics -- all metrics in json format /json/metrics/device/{DeviceName} -- particular device statistics in json format /json/metrics/pci-addr/{PciAddr} -- particular pci-address statistics in json format Test Plan: PASS: GO linting PASS: Unit test PASS: Api test. PASS: Docker image build process defined here [1] PASS: Created container image of this app, pushed to local registry and deployed on AIO-SX lab using sample deployment file. Then tested the APIs and validated the results. Story: 2010918 Task: 48794 [1]https://docs.starlingx.io/developer_resources/build_docker_image.html Change-Id: I5229b338b9e9afff3b02fe2389cfcd0c4e0590f6 Signed-off-by: AbhishekJ <abhishek.jaiswal@windriver.com>
This commit is contained in:
parent
b718b0e230
commit
4af59c5c7d
4
.gitignore
vendored
4
.gitignore
vendored
@ -33,3 +33,7 @@ cover
|
|||||||
AUTHORS
|
AUTHORS
|
||||||
ChangeLog
|
ChangeLog
|
||||||
*.sqlite
|
*.sqlite
|
||||||
|
**bin
|
||||||
|
**go-compose
|
||||||
|
*.log
|
||||||
|
coverage.*
|
70
README.md
Normal file
70
README.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# app-node-interface-metrics-exporter
|
||||||
|
app-node-interface-metrics-exporter flux cd app
|
||||||
|
|
||||||
|
#### Top Level Directory Structure
|
||||||
|
```bash
|
||||||
|
├── app-node-interface-metrics-exporter # Root Folder
|
||||||
|
│ ├── debian_build_layer.cfg
|
||||||
|
│ ├── debian_iso_image.inc
|
||||||
|
│ ├── debian_pkg_dirs
|
||||||
|
│ ├── metrics-exporter-api # Go code for api server which will expose Metrics for NIC .
|
||||||
|
│ ├── python3-k8sapp-node-interface-metrics-exporter # lifecycle managemnt code to support flux apps
|
||||||
|
│ ├── README.md
|
||||||
|
│ ├── requirements.txt
|
||||||
|
│ ├── stx-node-interface-metrics-exporter-helm # helm Package manager for the app
|
||||||
|
│ ├── test-requirements.txt
|
||||||
|
│ └── tox.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
> all command related to go code should run from `app-node-interface-metrics-exporter/metrics-exporter-api/docker/metrics-exporter-api`
|
||||||
|
### About metrics-exporter-api
|
||||||
|
It is Simple Http Server which reads network interface (PCI devices) and exposes following API's
|
||||||
|
|
||||||
|
1) http://{hostname}:{port}/metrics -- all node metrics in [OpenMetrics] format
|
||||||
|
2) http://{hostname}:{port}/metrics/device/{DeviceName} -- specified interface metrics identified by device name in [OpenMetrics] format
|
||||||
|
3) http://{hostname}:{port}/metrics/pci-addr/{PciAddr} -- specified interface metrics identified by PCI address in [OpenMetrics] format
|
||||||
|
4) http://{hostname}:{port}/json/metrics -- all node metrics in [JSON] format
|
||||||
|
5) http://{hostname}:{port}/json/metrics/device/{DeviceName} -- specified interface metrics identified by device name in [JSON] format
|
||||||
|
6) http://{hostname}:{port}/json/metrics/pci-addr/{PciAddr} -- specified interface metrics identified by PCI address in [JSON] format
|
||||||
|
|
||||||
|
#### Makefile Support
|
||||||
|
```bash
|
||||||
|
$ make
|
||||||
|
help: Show this help.
|
||||||
|
install_dep: install go dependency
|
||||||
|
run: run app on host machine
|
||||||
|
test: run go unit test
|
||||||
|
testcov: run go coverage test
|
||||||
|
vet: run go vet
|
||||||
|
lint: run go lint
|
||||||
|
build_linux: Build application
|
||||||
|
build_image: Build docker image
|
||||||
|
```
|
||||||
|
> `$ make run ` will start go dev http server
|
||||||
|
|
||||||
|
#### Run time Options / params for the metrics-exporter-app usage
|
||||||
|
| Options | Help |
|
||||||
|
| ------ | ------ |
|
||||||
|
| `-log.file` | Log file name (default "node_metrics_api.log") |
|
||||||
|
| `-log.level` | log level. (default "info"). Valid options trace, debug, info, warning, error, fatal and panic |
|
||||||
|
| `-log.file` | Log file name (default "node_metrics_api.log") |
|
||||||
|
| `-web.listen-address` | Port to listen on for web interface. (default ":9110") |
|
||||||
|
| `-path.sys` | mounted path for host /sys inside container (default "/sys") |
|
||||||
|
|
||||||
|
#### Local / Devlopment Set UP for metrics-exporter-api
|
||||||
|
`pre requisite go 1.21.0`
|
||||||
|
|
||||||
|
#### Container image reference for helm
|
||||||
|
[Dockerfile](/metrics-exporter-api/debian/Dockerfile)
|
||||||
|
|
||||||
|
[Build Reference](/metrics-exporter-api/debian/metrics-exporter-api.stable_docker_image)
|
||||||
|
|
||||||
|
#### References
|
||||||
|
[StarlingX](https://www.starlingx.io/)
|
||||||
|
|
||||||
|
[How to add a FluxCD App to Starlingx](https://wiki.openstack.org/wiki/StarlingX/Containers/HowToAddNewFluxCDAppInSTX)
|
||||||
|
|
||||||
|
[OpenMetrics](https://openmetrics.io/)
|
||||||
|
|
||||||
|
[JSON]: <https://www.json.org/>
|
||||||
|
[OpenMetrics]: <https://openmetrics.io/>
|
1
debian_stable_docker_images.inc
Normal file
1
debian_stable_docker_images.inc
Normal file
@ -0,0 +1 @@
|
|||||||
|
metrics-exporter-api
|
41
metrics-exporter-api/debian/Dockerfile
Normal file
41
metrics-exporter-api/debian/Dockerfile
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
# ARG BASE
|
||||||
|
# FROM ${BASE}
|
||||||
|
|
||||||
|
# Build Stage
|
||||||
|
FROM golang:alpine3.17 as buildstage
|
||||||
|
|
||||||
|
# Set destination for COPY
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY metrics-exporter-api /app/
|
||||||
|
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# Build
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -o /metrics-api-server
|
||||||
|
|
||||||
|
# Deploy binary which will make image size smaller
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
# Set workdir context of current path wrt image
|
||||||
|
WORKDIR /
|
||||||
|
|
||||||
|
COPY --from=buildstage /metrics-api-server /metrics-api-server
|
||||||
|
|
||||||
|
# Optional:
|
||||||
|
# To bind to a TCP port, runtime parameters must be supplied to the
|
||||||
|
# docker command.
|
||||||
|
# But we can document in the Dockerfile what ports
|
||||||
|
# the application is going to listen on by default.
|
||||||
|
# https://docs.docker.com/engine/reference/builder/#expose
|
||||||
|
EXPOSE 9110
|
||||||
|
|
||||||
|
ENTRYPOINT ["/metrics-api-server"]
|
@ -0,0 +1,4 @@
|
|||||||
|
BUILDER=docker
|
||||||
|
LABEL=metrics-exporter-api
|
||||||
|
DOCKER_CONTEXT=../docker
|
||||||
|
DOCKER_FILE=./Dockerfile
|
@ -0,0 +1,45 @@
|
|||||||
|
# Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
# All Rights Reserved.
|
||||||
|
run:
|
||||||
|
deadline: 5m
|
||||||
|
tests: true
|
||||||
|
skip-dirs-use-default: true
|
||||||
|
skip-files-use-default: true
|
||||||
|
skip-dirs:
|
||||||
|
- vendor
|
||||||
|
skip-files:
|
||||||
|
- bindata.go
|
||||||
|
linters:
|
||||||
|
enable-all: false
|
||||||
|
enable:
|
||||||
|
- unused
|
||||||
|
- revive
|
||||||
|
- deadcode
|
||||||
|
- gosec
|
||||||
|
- govet
|
||||||
|
- goimports
|
||||||
|
- gomodguard
|
||||||
|
- dupword
|
||||||
|
- godox
|
||||||
|
- misspell
|
||||||
|
- decorder
|
||||||
|
- gofmt
|
||||||
|
- sloglint
|
||||||
|
- tagalign
|
||||||
|
# Enable presets.
|
||||||
|
# https://golangci-lint.run/usage/linters
|
||||||
|
presets:
|
||||||
|
- bugs
|
||||||
|
- complexity
|
||||||
|
- error
|
||||||
|
- metalinter
|
||||||
|
- performance
|
||||||
|
- sql
|
||||||
|
- test
|
||||||
|
- unused
|
||||||
|
# Run only fast linters from enabled linters set (first run won't be fast)
|
||||||
|
# Default: false
|
||||||
|
fast: true
|
45
metrics-exporter-api/docker/metrics-exporter-api/Makefile
Normal file
45
metrics-exporter-api/docker/metrics-exporter-api/Makefile
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
# variables
|
||||||
|
GO ?= go
|
||||||
|
GOCOVER ?= $(GO) tool cover
|
||||||
|
LABEL ?= metrics-exporter-api
|
||||||
|
|
||||||
|
# Targets
|
||||||
|
help: ## Show this help.
|
||||||
|
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'
|
||||||
|
|
||||||
|
apphelp: ## Show this help.
|
||||||
|
$(GO) run . -h
|
||||||
|
|
||||||
|
install_dep: ## install go dependency
|
||||||
|
$(GO) mod tidy
|
||||||
|
|
||||||
|
run: ## run app on host machine
|
||||||
|
$(GO) run .
|
||||||
|
|
||||||
|
test: ## run go unit test
|
||||||
|
$(GO) test ./...
|
||||||
|
|
||||||
|
testcov: ## run go coverage test
|
||||||
|
$(GO) test -coverprofile=coverage.out ./...
|
||||||
|
$(GO) tool cover -func=coverage.out
|
||||||
|
$(GO) tool cover -html=coverage.out -o coverage.html
|
||||||
|
|
||||||
|
vet: ## run go vet
|
||||||
|
$(GO) vet
|
||||||
|
|
||||||
|
lint: ## run go lint
|
||||||
|
golangci-lint run
|
||||||
|
|
||||||
|
build_linux: ## Build application
|
||||||
|
CGO_ENABLED=0 GOOS=linux go build -o metrics-api-server
|
||||||
|
|
||||||
|
build_image: ## Build docker image
|
||||||
|
docker build -f ../../debian/Dockerfile -t starlingx/metrics-exporter-api ../
|
33
metrics-exporter-api/docker/metrics-exporter-api/go.mod
Normal file
33
metrics-exporter-api/docker/metrics-exporter-api/go.mod
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
// All Rights Reserved.
|
||||||
|
|
||||||
|
|
||||||
|
module opendev.org/starlingx/app-node-interface-metrics-exporter/metrics-exporter-api/docker/metrics-exporter-api
|
||||||
|
|
||||||
|
go 1.21.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bsm/openmetrics v0.3.1
|
||||||
|
github.com/gorilla/handlers v1.5.1
|
||||||
|
github.com/gorilla/mux v1.8.0
|
||||||
|
github.com/iancoleman/strcase v0.3.0
|
||||||
|
github.com/rs/cors v1.10.1
|
||||||
|
github.com/safchain/ethtool v0.3.0
|
||||||
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
github.com/stretchr/testify v1.7.0
|
||||||
|
github.com/vishvananda/netlink v1.1.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
|
||||||
|
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
|
||||||
|
golang.org/x/sys v0.6.0 // indirect
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||||
|
)
|
50
metrics-exporter-api/docker/metrics-exporter-api/go.sum
Normal file
50
metrics-exporter-api/docker/metrics-exporter-api/go.sum
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
github.com/bsm/openmetrics v0.3.1 h1:nhR6QgaKaDmnbnvVP9R0JyPExt8Qa+n1cJk/ouGC4FY=
|
||||||
|
github.com/bsm/openmetrics v0.3.1/go.mod h1:tabLMhjVjhdhFuwm9YenEVx0s54uvu56faEwYgD6L2g=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||||
|
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||||
|
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||||
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
|
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
|
||||||
|
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||||
|
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/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
|
||||||
|
github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||||
|
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
|
||||||
|
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
|
||||||
|
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/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
|
||||||
|
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||||
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
|
||||||
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
|
||||||
|
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
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=
|
63
metrics-exporter-api/docker/metrics-exporter-api/handler.go
Normal file
63
metrics-exporter-api/docker/metrics-exporter-api/handler.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/rs/cors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Function to create route/handler for web request
|
||||||
|
func allHandlers() http.Handler {
|
||||||
|
router := mux.NewRouter()
|
||||||
|
// allowed CORS
|
||||||
|
// more info https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
|
||||||
|
handler := cors.Default().Handler(router)
|
||||||
|
|
||||||
|
router.HandleFunc("/", rootGet).Methods("GET")
|
||||||
|
router.HandleFunc("/healthz", healthzGet).Methods("GET")
|
||||||
|
// Openmetrics endpoints
|
||||||
|
router.HandleFunc("/metrics", metricsGet).Methods("GET")
|
||||||
|
router.HandleFunc("/metrics/device/{DeviceName}", deviceGet).Methods("GET")
|
||||||
|
router.HandleFunc("/metrics/pci-addr/{PciAddr}", pciAddrGet).Methods("GET")
|
||||||
|
// json metrics endpoints
|
||||||
|
router.HandleFunc("/json/metrics", metricsGetJSON).Methods("GET")
|
||||||
|
router.HandleFunc("/json/metrics/device/{DeviceName}", deviceGetJSON).Methods("GET")
|
||||||
|
router.HandleFunc("/json/metrics/pci-addr/{PciAddr}", pciAddrGetJSON).Methods("GET")
|
||||||
|
|
||||||
|
return handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// this endpoint shows the uptime of the application
|
||||||
|
// it may be helpful to create probes in K8s
|
||||||
|
func healthzGet(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
response := fmt.Sprintf("uptime: %s\n", time.Since(time.Unix(0, StartupTime)))
|
||||||
|
_, err := w.Write([]byte(response))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since nothing is to show on root handler
|
||||||
|
func rootGet(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
response := fmt.Sprintf("/ root uptime: %s\n", time.Since(time.Unix(0, StartupTime)))
|
||||||
|
_, err := w.Write([]byte(response))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRootGet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
r, _ := http.NewRequest("GET", "/", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
rootGet(w, r)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHealthzGet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
r, _ := http.NewRequest("GET", "/healthz", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
healthzGet(w, r)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandlerFunc(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
}
|
120
metrics-exporter-api/docker/metrics-exporter-api/json_handler.go
Normal file
120
metrics-exporter-api/docker/metrics-exporter-api/json_handler.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// endpoint to get network metric of a node on which it is resides
|
||||||
|
// http://<hostname>:<port>/json/metrics
|
||||||
|
func metricsGetJSON(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
|
||||||
|
DeviceStat := ListAllNetDev()
|
||||||
|
// convert the map to a JSON encoded byte slice
|
||||||
|
jsonContent, mErr := json.Marshal(DeviceStat)
|
||||||
|
if mErr != nil {
|
||||||
|
fmt.Println(mErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, err := w.Write(jsonContent)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// endpoint to fetch metrics related to given network
|
||||||
|
// device by name
|
||||||
|
// http://<hostname>:<port>/json/device/<DeviceName>
|
||||||
|
func deviceGetJSON(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
params := mux.Vars(r)
|
||||||
|
DeviceName := params["DeviceName"]
|
||||||
|
allDeviceStat := ListAllNetDev()
|
||||||
|
|
||||||
|
devStats, ok := allDeviceStat[DeviceName]
|
||||||
|
|
||||||
|
// If the key exists
|
||||||
|
if ok {
|
||||||
|
// convert the map to a JSON encoded byte slice
|
||||||
|
jsonContent, mErr := json.Marshal(devStats)
|
||||||
|
if mErr != nil {
|
||||||
|
log.Error(mErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the byte slice to a string
|
||||||
|
// jsonString := string(jsonContent)
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, err := w.Write(jsonContent)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
fmt.Fprintf(w, " %s Device Not Found ", DeviceName)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// endpoint to fetch metrics related to given network
|
||||||
|
// device by pci addr
|
||||||
|
// http://<hostname>:<port>/json/pci-addr/<PciAddr>
|
||||||
|
func pciAddrGetJSON(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
found := false
|
||||||
|
params := mux.Vars(r)
|
||||||
|
PciAddr := params["PciAddr"]
|
||||||
|
allDeviceStat := ListAllNetDev()
|
||||||
|
|
||||||
|
for _, dev := range allDeviceStat {
|
||||||
|
|
||||||
|
if dev.Pciaddr == PciAddr {
|
||||||
|
found = true
|
||||||
|
// convert the map to a JSON encoded byte slice
|
||||||
|
jsonContent, mErr := json.Marshal(dev)
|
||||||
|
if mErr != nil {
|
||||||
|
log.Error(mErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the byte slice to a string
|
||||||
|
// jsonString := string(jsonContent)
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
_, err := w.Write(jsonContent)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
fmt.Fprintf(w, " %s Pci Addr Not found ", PciAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMetricsGetJson(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// /device/{DeviceName}
|
||||||
|
r, _ := http.NewRequest("GET", "/json/metrics/", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
metricsGetJSON(w, r)
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, w.Header().Get("Content-Type"), "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeviceGetJson(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// /device/{DeviceName}
|
||||||
|
r, _ := http.NewRequest("GET", "/json/device/", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Hack to try to fake gorilla/mux vars
|
||||||
|
vars := map[string]string{
|
||||||
|
"DeviceName": "eth0",
|
||||||
|
}
|
||||||
|
|
||||||
|
r = mux.SetURLVars(r, vars)
|
||||||
|
|
||||||
|
deviceGetJSON(w, r)
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
assert.Equal(t, w.Header().Get("Content-Type"), "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeviceGetJsonNeg(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// /device/{DeviceName}
|
||||||
|
r, _ := http.NewRequest("GET", "/json/device/", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Hack to try to fake gorilla/mux vars
|
||||||
|
vars := map[string]string{
|
||||||
|
"DeviceName": "404",
|
||||||
|
}
|
||||||
|
|
||||||
|
r = mux.SetURLVars(r, vars)
|
||||||
|
|
||||||
|
deviceGetJSON(w, r)
|
||||||
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPciAddrGetJsonNeg(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
r, _ := http.NewRequest("GET", "/json/pci-addr/", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Hack to try to fake gorilla/mux vars
|
||||||
|
vars := map[string]string{
|
||||||
|
"PciAddr": "11:00:00",
|
||||||
|
}
|
||||||
|
|
||||||
|
r = mux.SetURLVars(r, vars)
|
||||||
|
|
||||||
|
pciAddrGetJSON(w, r)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||||
|
}
|
61
metrics-exporter-api/docker/metrics-exporter-api/main.go
Normal file
61
metrics-exporter-api/docker/metrics-exporter-api/main.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/handlers"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
handler := allHandlers()
|
||||||
|
|
||||||
|
// StartUp Time used to calculate Uptime for the App
|
||||||
|
atomic.StoreInt64(&StartupTime, time.Now().UnixNano())
|
||||||
|
|
||||||
|
// Parse the flags or commandline arguments
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// log setup
|
||||||
|
logFile := OpenFile(*logFileName)
|
||||||
|
// close file on exit
|
||||||
|
defer logFile.Close()
|
||||||
|
logOutput := io.MultiWriter(os.Stdout, logFile)
|
||||||
|
log.SetOutput(logOutput)
|
||||||
|
logLevel, _ := log.ParseLevel(*logLevel)
|
||||||
|
// Only log the debug severity or above
|
||||||
|
log.SetLevel(logLevel)
|
||||||
|
|
||||||
|
// Print all flags what valuse is used
|
||||||
|
flag.VisitAll(func(f *flag.Flag) {
|
||||||
|
log.Infof("%s: %s", f.Name, f.Value)
|
||||||
|
})
|
||||||
|
|
||||||
|
loggedRouter := handlers.LoggingHandler(os.Stdout, handler)
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: *addr,
|
||||||
|
ReadHeaderTimeout: 3 * time.Second,
|
||||||
|
Handler: loggedRouter,
|
||||||
|
}
|
||||||
|
// Starting Http server
|
||||||
|
err := server.ListenAndServe()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// on exit log
|
||||||
|
log.Info("Server stopped\n")
|
||||||
|
}
|
@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/bsm/openmetrics"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/iancoleman/strcase"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// endpoint to get all network metric of a node on which it is resides
|
||||||
|
// http://<hostname>:<port>/metrics
|
||||||
|
func metricsGet(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
|
||||||
|
openMetContent := allDevInfoOpenMet()
|
||||||
|
w.Header().Set("Content-Type", OpenMetContentType)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
_, err := w.Write([]byte(openMetContent))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// endpoint to fetch metrics related to given network
|
||||||
|
// device by name
|
||||||
|
// http://<hostname>:<port>/device/<DeviceName>
|
||||||
|
func deviceGet(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
params := mux.Vars(r)
|
||||||
|
devName := params["DeviceName"]
|
||||||
|
allDeviceStat := ListAllNetDev()
|
||||||
|
|
||||||
|
devStats, ok := allDeviceStat[devName]
|
||||||
|
|
||||||
|
// If the key exists
|
||||||
|
if ok {
|
||||||
|
devStatOpnFmt := devStatOpenMet(devStats)
|
||||||
|
w.Header().Set("Content-Type", OpenMetContentType)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, err := w.Write([]byte(devStatOpnFmt))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Content-Type", OpenMetContentType)
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
fmt.Fprintf(w, " %s Device Not Found ", devName)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// endpoint to fetch metrics related to given network
|
||||||
|
// device by pci addr
|
||||||
|
// http://<hostname>:<port>/pci-addr/<PciAddr>
|
||||||
|
func pciAddrGet(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
found := false
|
||||||
|
params := mux.Vars(r)
|
||||||
|
PciAddr := params["PciAddr"]
|
||||||
|
allDeviceStat := ListAllNetDev()
|
||||||
|
|
||||||
|
for _, devStats := range allDeviceStat {
|
||||||
|
|
||||||
|
if devStats.Pciaddr == PciAddr {
|
||||||
|
found = true
|
||||||
|
DevStatOpnFmt := devStatOpenMet(devStats)
|
||||||
|
w.Header().Set("Content-Type", OpenMetContentType)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, err := w.Write([]byte(DevStatOpnFmt))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
w.Header().Set("Content-Type", OpenMetContentType)
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
fmt.Fprintf(w, " %s Pci Addr Not found ", PciAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to register and update network device info
|
||||||
|
// and statistics to Openmetrics format
|
||||||
|
func allDevInfoOpenMet() string {
|
||||||
|
|
||||||
|
// get all device info from lspci command
|
||||||
|
allDeviceInfo := ListAllNetDev()
|
||||||
|
// store stats data in openmetrics
|
||||||
|
var openMetContent string
|
||||||
|
|
||||||
|
for _, device := range allDeviceInfo {
|
||||||
|
openMetContent += devStatOpenMet(device)
|
||||||
|
}
|
||||||
|
|
||||||
|
return openMetContent
|
||||||
|
}
|
||||||
|
|
||||||
|
func devStatOpenMet(devInfo DevInfo) string {
|
||||||
|
var reg = openmetrics.NewRegistry()
|
||||||
|
var openMetVar = regDevInfo(reg)
|
||||||
|
|
||||||
|
openMetVar.With(
|
||||||
|
devInfo.Name,
|
||||||
|
devInfo.HardwareAddr,
|
||||||
|
devInfo.Broadcast,
|
||||||
|
// devInfo.duplex,
|
||||||
|
devInfo.Alias,
|
||||||
|
devInfo.OperState,
|
||||||
|
devInfo.Pciaddr,
|
||||||
|
)
|
||||||
|
|
||||||
|
// convert all netlink.LinkStatistics Struct to map
|
||||||
|
fields := reflect.TypeOf(*devInfo.Statistics)
|
||||||
|
// valPtr := reflect.ValueOf(devInfo.Statistics)
|
||||||
|
values := reflect.Indirect(reflect.ValueOf(devInfo.Statistics))
|
||||||
|
// get the number of field for looping n times
|
||||||
|
num := fields.NumField()
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
field := fields.Field(i)
|
||||||
|
value := values.Field(i)
|
||||||
|
log.Debug("Type:", field.Type, ",", field.Name, "=", value, "\n")
|
||||||
|
|
||||||
|
// register counter
|
||||||
|
name := "network_interface_" + strcase.ToSnake(field.Name)
|
||||||
|
var newInfo = reg.Counter(openmetrics.Desc{
|
||||||
|
Name: name,
|
||||||
|
Help: name,
|
||||||
|
Labels: []string{"device"},
|
||||||
|
})
|
||||||
|
newInfo.With(
|
||||||
|
devInfo.Name,
|
||||||
|
).Add(float64(value.Uint()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// create buffer to return
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if _, err := reg.WriteTo(&buf); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
// buffer.String()
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to create Informational Openmetrics paramater / variable
|
||||||
|
// for Network Interface Device Info
|
||||||
|
func regDevInfo(reg *openmetrics.Registry) openmetrics.InfoFamily {
|
||||||
|
|
||||||
|
var deviceInfo = reg.Info(openmetrics.Desc{
|
||||||
|
Name: "network_interface_device",
|
||||||
|
Help: "network_interface_device ",
|
||||||
|
Labels: []string{"name", "address", "broadcast", "ifalias", "operstate", "pciaddr"},
|
||||||
|
})
|
||||||
|
|
||||||
|
return deviceInfo
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMetricsGet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// /device/{DeviceName}
|
||||||
|
r, _ := http.NewRequest("GET", "/json/metrics/", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
metricsGet(w, r)
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeviceGet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// /device/{DeviceName}
|
||||||
|
r, _ := http.NewRequest("GET", "/json/device/", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Hack to try to fake gorilla/mux vars
|
||||||
|
vars := map[string]string{
|
||||||
|
"DeviceName": "eth0",
|
||||||
|
}
|
||||||
|
|
||||||
|
r = mux.SetURLVars(r, vars)
|
||||||
|
|
||||||
|
deviceGet(w, r)
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeviceGetNeg(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// /device/{DeviceName}
|
||||||
|
r, _ := http.NewRequest("GET", "/json/device/", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Hack to try to fake gorilla/mux vars
|
||||||
|
vars := map[string]string{
|
||||||
|
"DeviceName": "404",
|
||||||
|
}
|
||||||
|
|
||||||
|
r = mux.SetURLVars(r, vars)
|
||||||
|
|
||||||
|
deviceGet(w, r)
|
||||||
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPciAddrGetNeg(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
r, _ := http.NewRequest("GET", "/json/pci-addr/", nil)
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Hack to try to fake gorilla/mux vars
|
||||||
|
vars := map[string]string{
|
||||||
|
"PciAddr": "11:00:00",
|
||||||
|
}
|
||||||
|
|
||||||
|
r = mux.SetURLVars(r, vars)
|
||||||
|
|
||||||
|
pciAddrGet(w, r)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||||
|
}
|
92
metrics-exporter-api/docker/metrics-exporter-api/util.go
Normal file
92
metrics-exporter-api/docker/metrics-exporter-api/util.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/safchain/ethtool"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DevInfo Data structures to store Device info.
|
||||||
|
type DevInfo struct {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
HardwareAddr string
|
||||||
|
OperState string
|
||||||
|
EncapType string
|
||||||
|
Alias string
|
||||||
|
Pciaddr string
|
||||||
|
Broadcast string
|
||||||
|
Statistics *netlink.LinkStatistics
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFile function
|
||||||
|
func OpenFile(fileName string) *os.File {
|
||||||
|
logFile := fileName
|
||||||
|
// Open logfile
|
||||||
|
f, err := os.OpenFile(logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Failed to create logfile" + logFile)
|
||||||
|
log.Error(err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAllNetDev func to findout all the Pcie Devices and its statistics
|
||||||
|
// which have network or ethernet in Classname
|
||||||
|
// func ListAllPciNetDev(sysPath string) map[string]map[string]string {
|
||||||
|
func ListAllNetDev() map[string]DevInfo {
|
||||||
|
|
||||||
|
// make the list of dict containing DevInfo
|
||||||
|
allDeviceInfo := make(map[string]DevInfo)
|
||||||
|
|
||||||
|
allNetDevices, _ := netlink.LinkList()
|
||||||
|
for _, dev := range allNetDevices {
|
||||||
|
deviceName := dev.Attrs().Name
|
||||||
|
|
||||||
|
pciaddr, err := ethtool.BusInfo(dev.Attrs().Name)
|
||||||
|
if err != nil {
|
||||||
|
log.Infof(
|
||||||
|
"Unable to fetch Info from ethtool for %s, err: %s",
|
||||||
|
deviceName, err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var broadcast string
|
||||||
|
// try to fetch Broadcast Address
|
||||||
|
addr, err := netlink.AddrList(dev, netlink.NewRule().Family)
|
||||||
|
|
||||||
|
// we know that all device will not have Broadcast addr
|
||||||
|
// so just logged the name of devices which are not
|
||||||
|
if err != nil {
|
||||||
|
log.Info("Unable to fetch AddrList ", err)
|
||||||
|
}
|
||||||
|
if len(addr) > 0 {
|
||||||
|
broadcast = addr[0].Broadcast.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
allDeviceInfo[deviceName] = DevInfo{
|
||||||
|
Name: deviceName,
|
||||||
|
Type: dev.Type(),
|
||||||
|
HardwareAddr: dev.Attrs().HardwareAddr.String(),
|
||||||
|
OperState: dev.Attrs().OperState.String(),
|
||||||
|
EncapType: dev.Attrs().EncapType,
|
||||||
|
Alias: dev.Attrs().Alias,
|
||||||
|
Pciaddr: pciaddr,
|
||||||
|
Broadcast: broadcast,
|
||||||
|
Statistics: dev.Attrs().Statistics,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return allDeviceInfo
|
||||||
|
}
|
31
metrics-exporter-api/docker/metrics-exporter-api/variable.go
Normal file
31
metrics-exporter-api/docker/metrics-exporter-api/variable.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2023 Wind River Systems, Inc.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "flag"
|
||||||
|
|
||||||
|
var (
|
||||||
|
addr = flag.String(
|
||||||
|
"web.listen-address", ":9110", "Port to listen on for web interface.",
|
||||||
|
)
|
||||||
|
logLevel = flag.String(
|
||||||
|
"log.level", "info", "log level. Valid options trace,"+
|
||||||
|
" debug, info, warning, error, fatal and panic",
|
||||||
|
)
|
||||||
|
logFileName = flag.String(
|
||||||
|
"log.file", "node_metrics_api.log", "Log file name",
|
||||||
|
)
|
||||||
|
|
||||||
|
// StartupTime string
|
||||||
|
StartupTime int64
|
||||||
|
|
||||||
|
// OpenMetContentType content Type
|
||||||
|
// OpenMetContentType = "application/openmetrics-text; version=1.0.0; charset=utf-8"
|
||||||
|
OpenMetContentType = "text/plain"
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user