diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index f8eae93..9af3c4a 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -12,13 +12,19 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: 1.17 + - name: install stringer + run: go install golang.org/x/tools/cmd/stringer@latest + + - name: Generate + run: go generate ./... + - name: Build run: go build -v ./... diff --git a/.gitignore b/.gitignore index b72222b..745881e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ mystrom-exporter output/* +*_string.go # Created by https://www.toptal.com/developers/gitignore/api/osx,intellij+all,windows # Edit at https://www.toptal.com/developers/gitignore?templates=osx,intellij+all,windows @@ -148,4 +149,4 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -# End of https://www.toptal.com/developers/gitignore/api/osx,intellij+all,windows \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/osx,intellij+all,windows diff --git a/Dockerfile b/Dockerfile index 21f9383..7a797cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM golang:alpine as build WORKDIR /root COPY . /root -RUN go build . +RUN go install golang.org/x/tools/cmd/stringer@latest && go generate ./... && go build . FROM alpine:latest diff --git a/Makefile b/Makefile index ccd14c5..daa18af 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,56 @@ -.PHONY: clean run +## +# +.DEFAULT_GOAL := help +.PHONY: generate go-tools -linux: - GOOS=linux GOARCH=amd64 go build -o output/mystrom-exporter_linux-amd64 -mac: - GOOS=darwin GOARCH=amd64 go build -o output/mystrom-exporter_mac-amd64 -arm64: - GOOS=linux GOARCH=arm64 go build -o output/mystrom-exporter_linux-arm64 -arm: - GOOS=linux GOARCH=arm go build -o output/mystrom-exporter_linux-arm +version := $(shell git describe --tags --always) +revision := $(shell git rev-parse HEAD) +branch := $(shell git rev-parse --abbrev-ref HEAD) +builduser := $(shell whoami) +builddate := $(shell date '+%FT%T_%Z') + +versionPkgPrefix := mystrom-exporter/pkg/version + +BINDIR := $(CURDIR)/output +GO ?= go +GOPATH ?= $(shell $(GO) env GOPATH) +LDFLAGS := -w -s \ + -X $(versionPkgPrefix).Version=${version} \ + -X $(versionPkgPrefix).Revision=${revision} \ + -X $(versionPkgPrefix).Branch=${branch} \ + -X $(versionPkgPrefix).BuildUser=${builduser} \ + -X $(versionPkgPrefix).BuildDate=${builddate} +GOFLAGS := -v +GOX_FLAGS := -mod=vendor +GO_BUILD_FLAGS := -v +export GO111MODULE := on + +build: go-tools generate ## builds the all platform binaries of the exporter + $(GOPATH)/bin/gox \ + -os="darwin linux" \ + -arch="amd64 arm arm64" \ + -osarch="!darwin/arm" \ + -output "${BINDIR}/{{.Dir}}-{{.OS}}-{{.Arch}}" \ + -gcflags "$(GO_BUILD_FLAGS)" \ + -ldflags '$(LDFLAGS)' \ + -tags '$(TAGS)' \ + ./... + +run: + ${BINDIR}/mystrom-exporter-$(shell $(GO) env GOOS)-$(shell $(GO) env GOARCH) -all: linux mac arm64 arm \ No newline at end of file +generate: go-tools + $(GO) generate ./... + +go-tools: $(GOPATH)/bin/stringer $(GOPATH)/bin/gox + +# -- see more info on https://pkg.go.dev/golang.org/x/tools/cmd/stringer +$(GOPATH)/bin/stringer: + $(GO) install golang.org/x/tools/cmd/stringer@latest + +$(GOPATH)/bin/gox: + $(GO) install github.com/mitchellh/gox@latest +# -- +help: + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/README.md b/README.md index 1d28069..52a4561 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,31 @@ $ ./mystrom-exporter --help ``` | Flag | Description | Default | | ---- | ----------- | ------- | -| switch.ip-address | IP address of the switch you try to monitor | `` | | web.listen-address | Address to listen on | `:9452` | -| web.metrics-path | Path under which to expose metrics | `/metrics` | +| web.metrics-path | Path under which to expose exporters own metrics | `/metrics` | +| web.device-path | Path under which the metrics of the devices are fetched | `/device` | + +## Prometheus configuration +A enhancement has been made to have only one exporter which can scrape multiple devices. This is configured in +Prometheus as follows assuming we have 4 mystrom devices and the exporter is running locally on the smae machine as +the Prometheus. +```yaml + - job_name: mystrom + scrape_interval: 30s + metrics_path: /device + honor_labels: true + static_configs: + - targets: + - '192.168.105.11' + - '192.168.105.12' + - '192.168.105.13' + - '192.168.105.14' + relabel_configs: + - source_labels: [__address__] + target_label: __param_target + - target_label: __address__ + replacement: 127.0.0.1:9452 +``` ## Supported architectures Using the make file, you can easily build for the following architectures, those can also be considered the tested ones: @@ -40,6 +62,7 @@ Using the make file, you can easily build for the following architectures, those | Linux | arm64 | | Linux | arm | | Mac | amd64 | +| Mac | arm64 | Since go is cross compatible with windows, and mac arm as well, you should be able to build the binary for those as well, but they aren't tested. The docker image is only built & tested for amd64. diff --git a/go.mod b/go.mod index 1fdb4d1..5cf895f 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,10 @@ module mystrom-exporter go 1.15 -require github.com/prometheus/client_golang v1.10.0 +require ( + github.com/gorilla/mux v1.7.3 + github.com/prometheus/client_golang v1.10.0 + github.com/prometheus/common v0.18.0 + golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect + golang.org/x/tools v0.1.12 +) diff --git a/go.sum b/go.sum index a8c158b..042dbb0 100644 --- a/go.sum +++ b/go.sum @@ -7,9 +7,11 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -40,6 +42,7 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -89,6 +92,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -96,6 +100,7 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -138,10 +143,13 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= @@ -198,6 +206,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -237,6 +246,7 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -251,11 +261,13 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -275,6 +287,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -283,6 +296,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -300,6 +315,8 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -309,6 +326,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -328,11 +346,20 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/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.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -347,9 +374,13 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -376,9 +407,11 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -392,6 +425,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index f7f11ec..eae08f9 100644 --- a/main.go +++ b/main.go @@ -1,199 +1,155 @@ +//go:generate stringer -type MystromReqStatus main.go package main import ( - "encoding/json" "flag" "fmt" - "io/ioutil" - "log" "net/http" "os" + "strings" "time" + "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/prometheus/common/log" + + "mystrom-exporter/pkg/mystrom" + "mystrom-exporter/pkg/version" ) -type switchReport struct { - Power float64 `json:"power"` - WattPerSec float64 `json:"Ws"` - Relay bool `json:relay` - Temperature float64 `json:"temperature` -} +// -- MystromRequestStatusType represents the request to MyStrom device status +type MystromReqStatus uint32 -const namespace = "mystrom" +const ( + OK MystromReqStatus = iota + ERROR_SOCKET + ERROR_TIMEOUT + ERROR_PARSING_VALUE +) + +const namespace = "mystrom_exporter" var ( listenAddress = flag.String("web.listen-address", ":9452", "Address to listen on") metricsPath = flag.String("web.metrics-path", "/metrics", - "Path under which to expose metrics") - switchIP = flag.String("switch.ip-address", "", - "IP address of the switch you try to monitor") - - up = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "up"), - "Was the last myStrom query successful.", - nil, nil, - ) - myStromPower = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "report_power"), - "The current power consumed by devices attached to the switch", - nil, nil, - ) - - myStromRelay = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "report_relay"), - "The current state of the relay (wether or not the relay is currently turned on)", - nil, nil, - ) - - myStromTemperature = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "report_temperatur"), - "The currently measured temperature by the switch. (Might initially be wrong, but will automatically correct itself over the span of a few hours)", - nil, nil, - ) - - myStromWattPerSec = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "report_watt_per_sec"), - "The average of energy consumed per second from last call this request", - nil, nil, - ) + "Path under which to expose exporters own metrics") + devicePath = flag.String("web.device-path", "/device", + "Path under which the metrics of the devices are fetched") + showVersion = flag.Bool("version", false, + "Show version information.") ) - -type Exporter struct { - myStromSwitchIp string -} - -func NewExporter(myStromSwitchIp string) *Exporter { - return &Exporter{ - myStromSwitchIp: myStromSwitchIp, - } -} - -func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { - ch <- up - ch <- myStromPower - ch <- myStromRelay - ch <- myStromTemperature - ch <- myStromWattPerSec -} - -func (e *Exporter) Collect(ch chan<- prometheus.Metric) { - ch <- prometheus.MustNewConstMetric( - up, prometheus.GaugeValue, 1, - ) - - e.FetchSwitchMetrics(e.myStromSwitchIp, ch) -} - -func (e *Exporter) FetchSwitchMetrics(switchIP string, ch chan<- prometheus.Metric) { - - report, err := FetchReport(switchIP) - - if err != nil { - log.Printf("Error occured, while fetching metrics: %s", err) - ch <- prometheus.MustNewConstMetric( - up, prometheus.GaugeValue, 0, - ) - return - } - - ch <- prometheus.MustNewConstMetric( - myStromPower, prometheus.GaugeValue, report.Power, - ) - - if report.Relay { - ch <- prometheus.MustNewConstMetric( - myStromRelay, prometheus.GaugeValue, 1, - ) - } else { - ch <- prometheus.MustNewConstMetric( - myStromRelay, prometheus.GaugeValue, 0, - ) - } - - ch <- prometheus.MustNewConstMetric( - myStromWattPerSec, prometheus.GaugeValue, report.WattPerSec, - ) - - ch <- prometheus.MustNewConstMetric( - myStromTemperature, prometheus.GaugeValue, report.Temperature, - ) - -} - -func FetchReport(switchIP string) (*switchReport, error) { - log.Printf("Trying to connect to switch at: %s\n", switchIP) - url := "http://" + switchIP + "/report" - - switchClient := http.Client{ - Timeout: time.Second * 5, // 3 second timeout, might need to be increased - } - - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - return nil, err - } - - req.Header.Set("User-Agent", "myStrom-exporter") - - res, getErr := switchClient.Do(req) - if getErr != nil { - log.Printf("Error while trying to connect to switch: %s\n", getErr) - return nil, getErr - - } - - if res.Body != nil { - defer res.Body.Close() - } - - body, readErr := ioutil.ReadAll(res.Body) - if readErr != nil { - log.Printf("Error while reading body: %s\n", readErr) - return nil, readErr - } - - report := switchReport{} - err = json.Unmarshal(body, &report) - if err != nil { - log.Printf("Error while unmarshaling report: %s\n", err) - return nil, err - } - - return &report, nil -} +var ( + mystromDurationCounterVec *prometheus.CounterVec + mystromRequestsCounterVec *prometheus.CounterVec +) +var landingPage = []byte(` +myStrom switch report Exporter + +

myStrom Exporter

+

Metrics

+ +`) func main() { flag.Parse() - if *switchIP == "" { - flag.Usage() - fmt.Println("\nNo switch.ip-address provided") - os.Exit(1) + // -- show version information + if *showVersion { + v, err := version.Print("mystrom_exporter") + if err != nil { + log.Fatalf("Failed to print version information: %#v", err) + } + + fmt.Fprintln(os.Stdout, v) + os.Exit(0) } - exporter := NewExporter(*switchIP) - prometheus.MustRegister(exporter) + // -- create a new registry for the exporter telemetry + telemetryRegistry := setupMetrics() - http.Handle(*metricsPath, promhttp.Handler()) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(` - myStrom switch report Exporter - -

myStrom Exporter

-

Metrics

- - `)) + router := mux.NewRouter() + router.Handle(*metricsPath, promhttp.HandlerFor(telemetryRegistry, promhttp.HandlerOpts{})) + router.HandleFunc(*devicePath, scrapeHandler) + router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write(landingPage) }) + log.Infoln("Listening on address " + *listenAddress) + log.Fatal(http.ListenAndServe(*listenAddress, router)) +} - _, err := FetchReport(*switchIP) - if err != nil { - log.Fatalf("Switch at address %s couldn't be reached. Ensure it is reachable before starting the exporter", *switchIP) +func scrapeHandler(w http.ResponseWriter, r *http.Request) { + target := r.URL.Query().Get("target") + if target == "" { + http.Error(w, "'target' parameter must be specified", http.StatusBadRequest) + return } - log.Printf("Starting listener on %s\n", *listenAddress) - log.Fatal(http.ListenAndServe(*listenAddress, nil)) + log.Infof("got scrape request for target '%v'", target) + exporter := mystrom.NewExporter(target) + + start := time.Now() + gatherer, err := exporter.Scrape() + duration := time.Since(start).Seconds() + if err != nil { + if strings.Contains(fmt.Sprintf("%v", err), "unable to connect with target") { + mystromRequestsCounterVec.WithLabelValues(target, ERROR_SOCKET.String()).Inc() + } else if strings.Contains(fmt.Sprintf("%v", err), "i/o timeout") { + mystromRequestsCounterVec.WithLabelValues(target, ERROR_TIMEOUT.String()).Inc() + } else { + mystromRequestsCounterVec.WithLabelValues(target, ERROR_PARSING_VALUE.String()).Inc() + } + http.Error( + w, + fmt.Sprintf("failed to scrape target '%v': %v", target, err), + http.StatusInternalServerError, + ) + log.Error(err) + return + } + mystromDurationCounterVec.WithLabelValues(target).Add(duration) + mystromRequestsCounterVec.WithLabelValues(target, OK.String()).Inc() + + promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{}).ServeHTTP(w, r) +} + +// -- setupMetrics creates a new registry for the exporter telemetry +func setupMetrics() *prometheus.Registry { + registry := prometheus.NewRegistry() + registry.MustRegister(prometheus.NewGoCollector()) + registry.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{})) + + mystromDurationCounterVec = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: namespace, + Name: "request_duration_seconds_total", + Help: "Total duration of mystrom successful requests by target in seconds", + }, + []string{"target"}) + registry.MustRegister(mystromDurationCounterVec) + + mystromRequestsCounterVec = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: namespace, + Name: "requests_total", + Help: "Number of mystrom request by status and target", + }, + []string{"target", "status"}) + registry.MustRegister(mystromRequestsCounterVec) + + // -- make the build information is available through a metric + buildInfo := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "build_info", + Help: "A metric with a constant '1' value labeled by build information.", + }, + []string{"version", "revision", "branch", "goversion", "builddate", "builduser"}, + ) + buildInfo.WithLabelValues(version.Version, version.Revision, version.Branch, version.GoVersion, version.BuildDate, version.BuildUser).Set(1) + registry.MustRegister(buildInfo) + + return registry } diff --git a/pkg/mystrom/mystrom.go b/pkg/mystrom/mystrom.go new file mode 100644 index 0000000..3d789f1 --- /dev/null +++ b/pkg/mystrom/mystrom.go @@ -0,0 +1,214 @@ +package mystrom + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "time" + + "github.com/prometheus/common/log" + + "github.com/prometheus/client_golang/prometheus" +) + +const namespace = "mystrom" + +// 5 second timeout, might need to be increased +const reqTimeout = time.Second * 5 + +type switchReport struct { + Power float64 `json:"power"` + WattPerSec float64 `json:"Ws"` + Relay bool `json:"relay"` + Temperature float64 `json:"temperature"` +} + +type switchInfo struct { + Version string `json:"version"` + Mac string `json:"mac"` + SwType float64 `json:"type"` + SSID string `json:"ssid"` + Static bool `json:"static"` + Connected bool `json:"connected"` +} + +// Exporter -- +type Exporter struct { + myStromSwitchIp string + switchType float64 +} + +// NewExporter -- +func NewExporter(switchIP string) *Exporter { + return &Exporter{ + myStromSwitchIp: switchIP, + } +} + +// Scrape -- +func (e *Exporter) Scrape() (prometheus.Gatherer, error) { + reg := prometheus.NewRegistry() + + // -- + bodyInfo, err := e.fetchData("/api/v1/info") + if err != nil { + } + + info := switchInfo{} + err = json.Unmarshal(bodyInfo, &info) + if err != nil { + // fmt.Println(err) + // ch <- prometheus.MustNewConstMetric( + // up, prometheus.GaugeValue, 0, + // ) + return reg, fmt.Errorf("unable to decode switchReport: %v", err.Error()) + } + log.Debugf("info: %#v", info) + e.switchType = info.SwType + + if err := registerInfoMetrics(reg, info, e.myStromSwitchIp); err != nil { + return nil, fmt.Errorf("failed to register metrics : %v", err.Error()) + } + + // -- + bodyData, err := e.fetchData("/report") + if err != nil { + } + + report := switchReport{} + err = json.Unmarshal(bodyData, &report) + if err != nil { + // fmt.Println(err) + // ch <- prometheus.MustNewConstMetric( + // up, prometheus.GaugeValue, 0, + // ) + return reg, fmt.Errorf("unable to decode switchReport: %v", err.Error()) + } + log.Debugf("report: %#v", report) + + if err := registerMetrics(reg, report, e.myStromSwitchIp, e.switchType); err != nil { + return nil, fmt.Errorf("failed to register metrics : %v", err.Error()) + } + + return reg, nil +} + +// fetchData -- get the data from the switch under the given path +func (e *Exporter) fetchData(urlpath string) ([]byte, error) { + url := "http://" + e.myStromSwitchIp + urlpath + + switchClient := http.Client{ + Timeout: reqTimeout, + Transport: &http.Transport{ + DisableCompression: true, + }, + } + + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + // ch <- prometheus.MustNewConstMetric( + // up, prometheus.GaugeValue, 0, + // ) + } + req.Header.Set("User-Agent", "myStrom-exporter") + + res, getErr := switchClient.Do(req) + if getErr != nil { + // ch <- prometheus.MustNewConstMetric( + // up, prometheus.GaugeValue, 0, + // ) + } + if res.Body != nil { + defer res.Body.Close() + } + + body, readErr := ioutil.ReadAll(res.Body) + if readErr != nil { + // ch <- prometheus.MustNewConstMetric( + // up, prometheus.GaugeValue, 0, + // ) + return []byte{}, fmt.Errorf("unable to read body: %v", readErr.Error()) + } + + return body, nil +} + +// registerMetrics -- +func registerMetrics(reg prometheus.Registerer, data switchReport, target string, st float64) error { + + // -- + collectorRelay := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "relay", + Help: "The current state of the relay (wether or not the relay is currently turned on)", + }, + []string{"instance"}) + + if err := reg.Register(collectorRelay); err != nil { + return fmt.Errorf("failed to register metric %v: %v", "relay", err.Error()) + } + + if data.Relay { + collectorRelay.WithLabelValues(target).Set(1) + } else { + collectorRelay.WithLabelValues(target).Set(0) + } + + if st != 114 { + // -- + collectorPower := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "power", + Help: "The current power consumed by devices attached to the switch", + }, + []string{"instance"}) + + if err := reg.Register(collectorPower); err != nil { + return fmt.Errorf("failed to register metric %v: %v", "power", err.Error()) + } + + collectorPower.WithLabelValues(target).Set(data.Power) + + // -- + collectorTemperature := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "temperature", + Help: "The currently measured temperature by the switch. (Might initially be wrong, but will automatically correct itself over the span of a few hours)", + }, + []string{"instance"}) + + if err := reg.Register(collectorTemperature); err != nil { + return fmt.Errorf("failed to register metric %v: %v", "temperature", err.Error()) + } + + collectorTemperature.WithLabelValues(target).Set(data.Temperature) + + } + + return nil +} + +// registerMetrics -- +func registerInfoMetrics(reg prometheus.Registerer, data switchInfo, target string) error { + + // -- + collectorInfo := prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: namespace, + Name: "info", + Help: "general information about the device", + }, + []string{"instance", "version", "mac", "type", "ssid"}) + + if err := reg.Register(collectorInfo); err != nil { + return fmt.Errorf("failed to register metric %v: %v", "info", err.Error()) + } + + collectorInfo.WithLabelValues(target, data.Version, data.Mac, fmt.Sprintf("%v", data.SwType), data.SSID).Set(1) + + return nil +} diff --git a/pkg/version/version.go b/pkg/version/version.go new file mode 100644 index 0000000..5d52c04 --- /dev/null +++ b/pkg/version/version.go @@ -0,0 +1,60 @@ +package version + +import ( + "bytes" + "fmt" + "runtime" + "strings" + "text/template" +) + +// Build information. Populated at build-time. +var ( + Version string + Revision string + Branch string + BuildUser string + BuildDate string + GoVersion = runtime.Version() +) + +// versionInfoTmpl contains the template used by Print. +var versionInfoTmpl = ` +{{.program}}, version {{.version}} (branch: {{.branch}}, revision: {{.revision}}) + build user: {{.buildUser}} + build date: {{.buildDate}} + go version: {{.goVersion}} +` + +// Print returns version information. +func Print(program string) (string, error) { + m := map[string]string{ + "program": program, + "version": Version, + "revision": Revision, + "branch": Branch, + "buildUser": BuildUser, + "buildDate": BuildDate, + "goVersion": GoVersion, + } + t, err := template.New("version").Parse(versionInfoTmpl) + if err != nil { + return "", err + } + + var buf bytes.Buffer + if err := t.ExecuteTemplate(&buf, "version", m); err != nil { + return "", err + } + return strings.TrimSpace(buf.String()), nil +} + +// Info returns version, branch and revision information. +func Info() string { + return fmt.Sprintf("(version=%s, branch=%s, revision=%s)", Version, Branch, Revision) +} + +// BuildContext returns goVersion, buildUser and buildDate information. +func BuildContext() string { + return fmt.Sprintf("(go=%s, user=%s, date=%s)", GoVersion, BuildUser, BuildDate) +} diff --git a/tools.go b/tools.go new file mode 100644 index 0000000..ace5f40 --- /dev/null +++ b/tools.go @@ -0,0 +1,7 @@ +// +build tools + +package main + +import ( + _ "golang.org/x/tools/cmd/stringer" +) \ No newline at end of file