diff --git a/.gitignore b/.gitignore
index 78c457c..c4ce58a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,7 @@ cover
AUTHORS
ChangeLog
*.sqlite
+**bin
+**go-compose
+*.log
+coverage.*
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c1ca28b
--- /dev/null
+++ b/README.md
@@ -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]:
+[OpenMetrics]:
\ No newline at end of file
diff --git a/debian_stable_docker_images.inc b/debian_stable_docker_images.inc
new file mode 100644
index 0000000..0f045e5
--- /dev/null
+++ b/debian_stable_docker_images.inc
@@ -0,0 +1 @@
+metrics-exporter-api
\ No newline at end of file
diff --git a/metrics-exporter-api/debian/Dockerfile b/metrics-exporter-api/debian/Dockerfile
new file mode 100644
index 0000000..c62fea8
--- /dev/null
+++ b/metrics-exporter-api/debian/Dockerfile
@@ -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"]
\ No newline at end of file
diff --git a/metrics-exporter-api/debian/metrics-exporter-api.stable_docker_image b/metrics-exporter-api/debian/metrics-exporter-api.stable_docker_image
new file mode 100644
index 0000000..7447eb0
--- /dev/null
+++ b/metrics-exporter-api/debian/metrics-exporter-api.stable_docker_image
@@ -0,0 +1,4 @@
+BUILDER=docker
+LABEL=metrics-exporter-api
+DOCKER_CONTEXT=../docker
+DOCKER_FILE=./Dockerfile
\ No newline at end of file
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/.golangci.yml b/metrics-exporter-api/docker/metrics-exporter-api/.golangci.yml
new file mode 100644
index 0000000..fbb5d6b
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/.golangci.yml
@@ -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
\ No newline at end of file
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/Makefile b/metrics-exporter-api/docker/metrics-exporter-api/Makefile
new file mode 100644
index 0000000..fd0e262
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/Makefile
@@ -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 ../
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/go.mod b/metrics-exporter-api/docker/metrics-exporter-api/go.mod
new file mode 100644
index 0000000..a7735de
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/go.mod
@@ -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
+)
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/go.sum b/metrics-exporter-api/docker/metrics-exporter-api/go.sum
new file mode 100644
index 0000000..5ddaae5
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/go.sum
@@ -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=
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/handler.go b/metrics-exporter-api/docker/metrics-exporter-api/handler.go
new file mode 100644
index 0000000..b401e79
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/handler.go
@@ -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)
+ }
+}
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/handler_test.go b/metrics-exporter-api/docker/metrics-exporter-api/handler_test.go
new file mode 100644
index 0000000..a5fe4c1
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/handler_test.go
@@ -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()
+
+}
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/json_handler.go b/metrics-exporter-api/docker/metrics-exporter-api/json_handler.go
new file mode 100644
index 0000000..96bb2b6
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/json_handler.go
@@ -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://:/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://:/json/device/
+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://:/json/pci-addr/
+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)
+ }
+
+}
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/json_handler_test.go b/metrics-exporter-api/docker/metrics-exporter-api/json_handler_test.go
new file mode 100644
index 0000000..66ea965
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/json_handler_test.go
@@ -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)
+}
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/main.go b/metrics-exporter-api/docker/metrics-exporter-api/main.go
new file mode 100644
index 0000000..1879cf5
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/main.go
@@ -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")
+}
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/openmet_handler.go b/metrics-exporter-api/docker/metrics-exporter-api/openmet_handler.go
new file mode 100644
index 0000000..024db67
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/openmet_handler.go
@@ -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://:/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://:/device/
+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://:/pci-addr/
+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
+}
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/openmet_handler_test.go b/metrics-exporter-api/docker/metrics-exporter-api/openmet_handler_test.go
new file mode 100644
index 0000000..d13938c
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/openmet_handler_test.go
@@ -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)
+}
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/util.go b/metrics-exporter-api/docker/metrics-exporter-api/util.go
new file mode 100644
index 0000000..271a626
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/util.go
@@ -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
+}
diff --git a/metrics-exporter-api/docker/metrics-exporter-api/variable.go b/metrics-exporter-api/docker/metrics-exporter-api/variable.go
new file mode 100644
index 0000000..3a07318
--- /dev/null
+++ b/metrics-exporter-api/docker/metrics-exporter-api/variable.go
@@ -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"
+)