pax_global_header00006660000000000000000000000064141541525120014512gustar00rootroot0000000000000052 comment=e34adbc1d5ec6d3691fd522f6061fb0bb2a7bb90 prometheus-ipmi-exporter-1.4.0+ds/000077500000000000000000000000001415415251200171135ustar00rootroot00000000000000prometheus-ipmi-exporter-1.4.0+ds/.cirrus.yml000066400000000000000000000007741415415251200212330ustar00rootroot00000000000000container: image: quay.io/prometheus/golang-builder:1.13-base build_task: build_script: make release_task: only_if: $CIRRUS_TAG =~ 'v.*' depends_on: build build_script: make build tarball tarball_artifacts: path: "ipmi_exporter-*.tar.gz" type: application/gzip matrix: - env: GOOS: linux GOARCH: amd64 - env: GOOS: darwin GOARCH: amd64 - env: GOOS: freebsd GOARCH: amd64 - env: GOOS: linux GOARCH: arm64 prometheus-ipmi-exporter-1.4.0+ds/.gitignore000066400000000000000000000000271415415251200211020ustar00rootroot00000000000000ipmi_exporter *.tar.gz prometheus-ipmi-exporter-1.4.0+ds/.promu.yml000066400000000000000000000012151415415251200210550ustar00rootroot00000000000000go: # Whenever the Go version is updated here, # .cirrus.yml should also be updated. version: 1.13 repository: path: github.com/soundcloud/ipmi_exporter build: flags: -mod=vendor -a -tags 'netgo static_build' ldflags: | -X github.com/prometheus/common/version.Version={{.Version}} -X github.com/prometheus/common/version.Revision={{.Revision}} -X github.com/prometheus/common/version.Branch={{.Branch}} -X github.com/prometheus/common/version.BuildUser={{user}}@{{host}} -X github.com/prometheus/common/version.BuildDate={{date "20060102-15:04:05"}} tarball: files: - LICENSE prometheus-ipmi-exporter-1.4.0+ds/CONTRIBUTING.md000066400000000000000000000034321415415251200213460ustar00rootroot00000000000000## Contributing to ipmi_exporter In the spirit of [free software][free-sw], **everyone** is encouraged to help improve this project. Here are some ways that *you* can contribute: * Use alpha, beta, and pre-released software versions. * Report bugs. * Suggest new features. * Write or edit documentation. * Write specifications. * Write code; **no patch is too small**: fix typos, add comments, add tests, clean up inconsistent whitespace. * Refactor code. * Fix [issues][]. * Review patches. ## Submitting an issue We use the [GitHub issue tracker][issues] to track bugs and features. Before you submit a bug report or feature request, check to make sure that it has not already been submitted. When you submit a bug report, include a [Gist][] that includes a stack trace and any details that might be necessary to reproduce the bug. ## Submitting a pull request 1. [Fork the repository][fork]. 2. [Create a topic branch][branch]. 3. Implement your feature or bug fix. 4. Unlike we did so far, maybe add tests and make sure that they completely cover your changes and potential edge cases. 5. If there are tests now, run `go test`. If your tests fail, revise your code and tests, and rerun `go test` until they pass. 6. Add documentation for your feature or bug fix in the code, documentation, or PR/commit message. 7. Commit and push your changes. 8. [Submit a pull request][pr] that includes a link to the [issues][] for which you are submitting a bug fix (if applicable). [branch]: http://learn.github.com/p/branching.html [fork]: http://help.github.com/fork-a-repo [free-sw]: http://www.fsf.org/licensing/essays/free-sw.html [gist]: https://gist.github.com [issues]: https://github.com/soundcloud/ipmi_exporter/issues [pr]: http://help.github.com/send-pull-requests prometheus-ipmi-exporter-1.4.0+ds/Dockerfile000066400000000000000000000010501415415251200211010ustar00rootroot00000000000000# Build /go/bin/ipmi_exporter FROM quay.io/prometheus/golang-builder:1.15-base AS builder ADD . /go/src/github.com/soundcloud/ipmi_exporter/ RUN cd /go/src/github.com/soundcloud/ipmi_exporter && make # Container image FROM ubuntu:18.04 WORKDIR / RUN apt-get update \ && apt-get install freeipmi-tools -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* COPY --from=builder /go/src/github.com/soundcloud/ipmi_exporter/ipmi_exporter /bin/ipmi_exporter EXPOSE 9290 ENTRYPOINT ["/bin/ipmi_exporter"] CMD ["--config.file", "/config.yml"] prometheus-ipmi-exporter-1.4.0+ds/LICENSE000066400000000000000000000021251415415251200201200ustar00rootroot00000000000000The MIT License Copyright (c) 2019 SoundCloud Ltd. and the IPMI exporter developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. prometheus-ipmi-exporter-1.4.0+ds/Makefile000066400000000000000000000003441415415251200205540ustar00rootroot00000000000000# Override the default common all. .PHONY: all all: precheck style unused build test DOCKER_ARCHS ?= amd64 DOCKER_IMAGE_NAME ?= ipmi-exporter DOCKER_REPO ?= soundcloud include Makefile.common docker: common-docker prometheus-ipmi-exporter-1.4.0+ds/Makefile.common000066400000000000000000000234651415415251200220540ustar00rootroot00000000000000# Copyright 2018 The Prometheus Authors # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # A common Makefile that includes rules to be reused in different prometheus projects. # !!! Open PRs only against the prometheus/prometheus/Makefile.common repository! # Example usage : # Create the main Makefile in the root project directory. # include Makefile.common # customTarget: # @echo ">> Running customTarget" # # Ensure GOBIN is not set during build so that promu is installed to the correct path unexport GOBIN GO ?= go GOFMT ?= $(GO)fmt FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH))) GOOPTS ?= GOHOSTOS ?= $(shell $(GO) env GOHOSTOS) GOHOSTARCH ?= $(shell $(GO) env GOHOSTARCH) GO_VERSION ?= $(shell $(GO) version) GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION)) PRE_GO_111 ?= $(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(10|[0-9])\.') GOVENDOR := GO111MODULE := ifeq (, $(PRE_GO_111)) ifneq (,$(wildcard go.mod)) # Enforce Go modules support just in case the directory is inside GOPATH (and for Travis CI). GO111MODULE := on ifneq (,$(wildcard vendor)) # Always use the local vendor/ directory to satisfy the dependencies. GOOPTS := $(GOOPTS) -mod=vendor endif endif else ifneq (,$(wildcard go.mod)) ifneq (,$(wildcard vendor)) $(warning This repository requires Go >= 1.11 because of Go modules) $(warning Some recipes may not work as expected as the current Go runtime is '$(GO_VERSION_NUMBER)') endif else # This repository isn't using Go modules (yet). GOVENDOR := $(FIRST_GOPATH)/bin/govendor endif endif PROMU := $(FIRST_GOPATH)/bin/promu pkgs = ./... ifeq (arm, $(GOHOSTARCH)) GOHOSTARM ?= $(shell GOARM= $(GO) env GOARM) GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)v$(GOHOSTARM) else GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH) endif GOTEST := $(GO) test GOTEST_DIR := ifneq ($(CIRCLE_JOB),) ifneq ($(shell which gotestsum),) GOTEST_DIR := test-results GOTEST := gotestsum --junitfile $(GOTEST_DIR)/unit-tests.xml -- endif endif PROMU_VERSION ?= 0.12.0 PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= GOLANGCI_LINT_VERSION ?= v1.39.0 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386)) GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint endif endif PREFIX ?= $(shell pwd) BIN_DIR ?= $(shell pwd) DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD)) DOCKERFILE_PATH ?= ./Dockerfile DOCKERBUILD_CONTEXT ?= ./ DOCKER_REPO ?= prom DOCKER_ARCHS ?= amd64 BUILD_DOCKER_ARCHS = $(addprefix common-docker-,$(DOCKER_ARCHS)) PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS)) TAG_DOCKER_ARCHS = $(addprefix common-docker-tag-latest-,$(DOCKER_ARCHS)) ifeq ($(GOHOSTARCH),amd64) ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux freebsd darwin windows)) # Only supported on amd64 test-flags := -race endif endif # This rule is used to forward a target like "build" to "common-build". This # allows a new "build" target to be defined in a Makefile which includes this # one and override "common-build" without override warnings. %: common-% ; .PHONY: common-all common-all: precheck style check_license lint unused build test .PHONY: common-style common-style: @echo ">> checking code style" @fmtRes=$$($(GOFMT) -d $$(find . -path ./vendor -prune -o -name '*.go' -print)); \ if [ -n "$${fmtRes}" ]; then \ echo "gofmt checking failed!"; echo "$${fmtRes}"; echo; \ echo "Please ensure you are using $$($(GO) version) for formatting code."; \ exit 1; \ fi .PHONY: common-check_license common-check_license: @echo ">> checking license header" @licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \ awk 'NR<=3' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \ done); \ if [ -n "$${licRes}" ]; then \ echo "license header checking failed:"; echo "$${licRes}"; \ exit 1; \ fi .PHONY: common-deps common-deps: @echo ">> getting dependencies" ifdef GO111MODULE GO111MODULE=$(GO111MODULE) $(GO) mod download else $(GO) get $(GOOPTS) -t ./... endif .PHONY: update-go-deps update-go-deps: @echo ">> updating Go dependencies" @for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \ $(GO) get $$m; \ done GO111MODULE=$(GO111MODULE) $(GO) mod tidy ifneq (,$(wildcard vendor)) GO111MODULE=$(GO111MODULE) $(GO) mod vendor endif .PHONY: common-test-short common-test-short: $(GOTEST_DIR) @echo ">> running short tests" GO111MODULE=$(GO111MODULE) $(GOTEST) -short $(GOOPTS) $(pkgs) .PHONY: common-test common-test: $(GOTEST_DIR) @echo ">> running all tests" GO111MODULE=$(GO111MODULE) $(GOTEST) $(test-flags) $(GOOPTS) $(pkgs) $(GOTEST_DIR): @mkdir -p $@ .PHONY: common-format common-format: @echo ">> formatting code" GO111MODULE=$(GO111MODULE) $(GO) fmt $(pkgs) .PHONY: common-vet common-vet: @echo ">> vetting code" GO111MODULE=$(GO111MODULE) $(GO) vet $(GOOPTS) $(pkgs) .PHONY: common-lint common-lint: $(GOLANGCI_LINT) ifdef GOLANGCI_LINT @echo ">> running golangci-lint" ifdef GO111MODULE # 'go list' needs to be executed before staticcheck to prepopulate the modules cache. # Otherwise staticcheck might fail randomly for some reason not yet explained. GO111MODULE=$(GO111MODULE) $(GO) list -e -compiled -test=true -export=false -deps=true -find=false -tags= -- ./... > /dev/null GO111MODULE=$(GO111MODULE) $(GOLANGCI_LINT) run $(GOLANGCI_LINT_OPTS) $(pkgs) else $(GOLANGCI_LINT) run $(pkgs) endif endif # For backward-compatibility. .PHONY: common-staticcheck common-staticcheck: lint .PHONY: common-unused common-unused: $(GOVENDOR) ifdef GOVENDOR @echo ">> running check for unused packages" @$(GOVENDOR) list +unused | grep . && exit 1 || echo 'No unused packages' else ifdef GO111MODULE @echo ">> running check for unused/missing packages in go.mod" GO111MODULE=$(GO111MODULE) $(GO) mod tidy ifeq (,$(wildcard vendor)) @git diff --exit-code -- go.sum go.mod else @echo ">> running check for unused packages in vendor/" GO111MODULE=$(GO111MODULE) $(GO) mod vendor @git diff --exit-code -- go.sum go.mod vendor/ endif endif endif .PHONY: common-build common-build: promu @echo ">> building binaries" GO111MODULE=$(GO111MODULE) $(PROMU) build --prefix $(PREFIX) $(PROMU_BINARIES) .PHONY: common-tarball common-tarball: promu @echo ">> building release tarball" $(PROMU) tarball --prefix $(PREFIX) $(BIN_DIR) .PHONY: common-docker $(BUILD_DOCKER_ARCHS) common-docker: $(BUILD_DOCKER_ARCHS) $(BUILD_DOCKER_ARCHS): common-docker-%: docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" \ -f $(DOCKERFILE_PATH) \ --build-arg ARCH="$*" \ --build-arg OS="linux" \ $(DOCKERBUILD_CONTEXT) .PHONY: common-docker-publish $(PUBLISH_DOCKER_ARCHS) common-docker-publish: $(PUBLISH_DOCKER_ARCHS) $(PUBLISH_DOCKER_ARCHS): common-docker-publish-%: docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" DOCKER_MAJOR_VERSION_TAG = $(firstword $(subst ., ,$(shell cat VERSION))) .PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS) common-docker-tag-latest: $(TAG_DOCKER_ARCHS) $(TAG_DOCKER_ARCHS): common-docker-tag-latest-%: docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest" docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)" .PHONY: common-docker-manifest common-docker-manifest: DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(DOCKER_IMAGE_TAG)) DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" .PHONY: promu promu: $(PROMU) $(PROMU): $(eval PROMU_TMP := $(shell mktemp -d)) curl -s -L $(PROMU_URL) | tar -xvzf - -C $(PROMU_TMP) mkdir -p $(FIRST_GOPATH)/bin cp $(PROMU_TMP)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(FIRST_GOPATH)/bin/promu rm -r $(PROMU_TMP) .PHONY: proto proto: @echo ">> generating code from proto files" @./scripts/genproto.sh ifdef GOLANGCI_LINT $(GOLANGCI_LINT): mkdir -p $(FIRST_GOPATH)/bin curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/$(GOLANGCI_LINT_VERSION)/install.sh \ | sed -e '/install -d/d' \ | sh -s -- -b $(FIRST_GOPATH)/bin $(GOLANGCI_LINT_VERSION) endif ifdef GOVENDOR .PHONY: $(GOVENDOR) $(GOVENDOR): GOOS= GOARCH= $(GO) get -u github.com/kardianos/govendor endif .PHONY: precheck precheck:: define PRECHECK_COMMAND_template = precheck:: $(1)_precheck PRECHECK_COMMAND_$(1) ?= $(1) $$(strip $$(PRECHECK_OPTIONS_$(1))) .PHONY: $(1)_precheck $(1)_precheck: @if ! $$(PRECHECK_COMMAND_$(1)) 1>/dev/null 2>&1; then \ echo "Execution of '$$(PRECHECK_COMMAND_$(1))' command failed. Is $(1) installed?"; \ exit 1; \ fi endef prometheus-ipmi-exporter-1.4.0+ds/README.md000066400000000000000000000073631415415251200204030ustar00rootroot00000000000000Prometheus IPMI Exporter ======================== [![Build Status](https://api.cirrus-ci.com/github/soundcloud/ipmi_exporter.svg?branch=master)](https://cirrus-ci.com/github/soundcloud/ipmi_exporter) This is an IPMI exporter for [Prometheus](https://prometheus.io). It supports both the regular `/metrics` endpoint, exposing metrics from the host that the exporter is running on, as well as an `/ipmi` endpoint that supports IPMI over RMCP - one exporter running on one host can be used to monitor a large number of IPMI interfaces by passing the `target` parameter to a scrape. The exporter relies on tools from the [FreeIPMI](https://www.gnu.org/software/freeipmi/) suite for the actual IPMI implementation. ## Installation For most use-cases, simply download the [the latest release](https://github.com/soundcloud/ipmi_exporter/releases). ### Building from source You need a Go development environment. Then, simply run `make` to build the executable: make This uses the common prometheus tooling to build and run some tests. Alternatively, you can use the standard Go tooling, which will install the executable in `$GOPATH/bin`: go get github.com/soundcloud/ipmi_exporter ### Building a Docker container You can build a Docker container with the included `docker` make target: make docker This will not even require Go tooling on the host. See the included [docker compose example](docker-compose.yml) for how to use the resulting container. ## Running A minimal invocation looks like this: ./ipmi_exporter Supported parameters include: - `web.listen-address`: the address/port to listen on (default: `":9290"`) - `config.file`: path to the configuration file (default: none) - `freeipmi.path`: path to the FreeIPMI executables (default: rely on `$PATH`) For syntax and a complete list of available parameters, run: ./ipmi_exporter -h Make sure you have the following tools from the [FreeIPMI](https://www.gnu.org/software/freeipmi/) suite installed: - `ipmimonitoring`/`ipmi-sensors` - `ipmi-dcmi` - `ipmi-raw` - `bmc-info` - `ipmi-sel` - `ipmi-chassis` ### Running as unprivileged user If you are running the exporter as unprivileged user, but need to execute the FreeIPMI tools as root, you can do the following: 1. Add sudoers files to permit the following commands ``` ipmi-exporter ALL = NOPASSWD: /usr/sbin/ipmimonitoring,\ /usr/sbin/ipmi-sensors,\ /usr/sbin/ipmi-dcmi,\ /usr/sbin/ipmi-raw,\ /usr/sbin/bmc-info,\ /usr/sbin/ipmi-chassis,\ /usr/sbin/ipmi-sel ``` 2. In your module config, override the collector command with `sudo` for every collector you are using and add the actual command as custom argument. Example for the "ipmi" collector: ```yaml collector_cmd: ipmi: sudo custom_args: ipmi: - "ipmimonitoring" ``` See the last module in the [example config](ipmi_remote.yml). ### Running in Docker **NOTE:** you should only use Docker for remote metrics. See [Building a Docker container](#building-a-docker-container) and the example `docker-compose.yml`. Edit the `ipmi_remote.yml` file to configure IPMI credentials, then run with: sudo docker-compose up -d By default, the server will bind on `0.0.0.0:9290`. ## Configuration The [configuration](docs/configuration.md) document describes both the configuration of the IPMI exporter itself as well as providing some guidance for configuring the Prometheus server to scrape it. ## Exported data For a description of the metrics that this exporter provides, see the [metrics](docs/metrics.md) document. prometheus-ipmi-exporter-1.4.0+ds/collector.go000066400000000000000000000043361415415251200214360ustar00rootroot00000000000000package main import ( "path" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/soundcloud/ipmi_exporter/freeipmi" ) const ( namespace = "ipmi" targetLocal = "" ) type collector interface { Name() CollectorName Cmd() string Args() []string Collect(output freeipmi.Result, ch chan<- prometheus.Metric, target ipmiTarget) (int, error) } type metaCollector struct { target string module string config *SafeConfig } type ipmiTarget struct { host string config IPMIConfig } var ( upDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "up"), "'1' if a scrape of the IPMI device was successful, '0' otherwise.", []string{"collector"}, nil, ) durationDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "scrape_duration", "seconds"), "Returns how long the scrape took to complete in seconds.", nil, nil, ) ) // Describe implements Prometheus.Collector. func (c metaCollector) Describe(ch chan<- *prometheus.Desc) { // all metrics are described ad-hoc } func markCollectorUp(ch chan<- prometheus.Metric, name string, up int) { ch <- prometheus.MustNewConstMetric( upDesc, prometheus.GaugeValue, float64(up), name, ) } // Collect implements Prometheus.Collector. func (c metaCollector) Collect(ch chan<- prometheus.Metric) { start := time.Now() defer func() { duration := time.Since(start).Seconds() log.Debugf("Scrape of target %s took %f seconds.", targetName(c.target), duration) ch <- prometheus.MustNewConstMetric( durationDesc, prometheus.GaugeValue, duration, ) }() config := c.config.ConfigForTarget(c.target, c.module) target := ipmiTarget{ host: c.target, config: config, } for _, collector := range config.GetCollectors() { var up int log.Debugf("Running collector: %s", collector.Name()) fqcmd := path.Join(*executablesPath, collector.Cmd()) args := collector.Args() cfg := config.GetFreeipmiConfig() result := freeipmi.Execute(fqcmd, args, cfg, target.host, log.Base()) up, _ = collector.Collect(result, ch, target) markCollectorUp(ch, string(collector.Name()), up) } } func targetName(target string) string { if target == targetLocal { return "[local]" } return target } prometheus-ipmi-exporter-1.4.0+ds/collector_bmc.go000066400000000000000000000031041415415251200222470ustar00rootroot00000000000000package main import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/soundcloud/ipmi_exporter/freeipmi" ) const ( BMCCollectorName CollectorName = "bmc" ) var ( bmcInfoDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "bmc", "info"), "Constant metric with value '1' providing details about the BMC.", []string{"firmware_revision", "manufacturer_id", "system_firmware_version"}, nil, ) ) type BMCCollector struct{} func (c BMCCollector) Name() CollectorName { return BMCCollectorName } func (c BMCCollector) Cmd() string { return "bmc-info" } func (c BMCCollector) Args() []string { return []string{} } func (c BMCCollector) Collect(result freeipmi.Result, ch chan<- prometheus.Metric, target ipmiTarget) (int, error) { firmwareRevision, err := freeipmi.GetBMCInfoFirmwareRevision(result) if err != nil { log.Errorf("Failed to collect BMC data from %s: %s", targetName(target.host), err) return 0, err } manufacturerID, err := freeipmi.GetBMCInfoManufacturerID(result) if err != nil { log.Errorf("Failed to collect BMC data from %s: %s", targetName(target.host), err) return 0, err } systemFirmwareVersion, err := freeipmi.GetBMCInfoSystemFirmwareVersion(result) if err != nil { // This one is not always available. log.Debugf("Failed to parse bmc-info data from %s: %s", targetName(target.host), err) systemFirmwareVersion = "N/A" } ch <- prometheus.MustNewConstMetric( bmcInfoDesc, prometheus.GaugeValue, 1, firmwareRevision, manufacturerID, systemFirmwareVersion, ) return 1, nil } prometheus-ipmi-exporter-1.4.0+ds/collector_chassis.go000066400000000000000000000021431415415251200231450ustar00rootroot00000000000000package main import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/soundcloud/ipmi_exporter/freeipmi" ) const ( ChassisCollectorName CollectorName = "chassis" ) var ( chassisPowerStateDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "chassis", "power_state"), "Current power state (1=on, 0=off).", []string{}, nil, ) ) type ChassisCollector struct{} func (c ChassisCollector) Name() CollectorName { return ChassisCollectorName } func (c ChassisCollector) Cmd() string { return "ipmi-chassis" } func (c ChassisCollector) Args() []string { return []string{"--get-chassis-status"} } func (c ChassisCollector) Collect(result freeipmi.Result, ch chan<- prometheus.Metric, target ipmiTarget) (int, error) { currentChassisPowerState, err := freeipmi.GetChassisPowerState(result) if err != nil { log.Errorf("Failed to collect chassis data from %s: %s", targetName(target.host), err) return 0, err } ch <- prometheus.MustNewConstMetric( chassisPowerStateDesc, prometheus.GaugeValue, currentChassisPowerState, ) return 1, nil } prometheus-ipmi-exporter-1.4.0+ds/collector_dcmi.go000066400000000000000000000021321415415251200224220ustar00rootroot00000000000000package main import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/soundcloud/ipmi_exporter/freeipmi" ) const ( DCMICollectorName CollectorName = "dcmi" ) var ( powerConsumptionDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "dcmi", "power_consumption_watts"), "Current power consumption in Watts.", []string{}, nil, ) ) type DCMICollector struct{} func (c DCMICollector) Name() CollectorName { return DCMICollectorName } func (c DCMICollector) Cmd() string { return "ipmi-dcmi" } func (c DCMICollector) Args() []string { return []string{"--get-system-power-statistics"} } func (c DCMICollector) Collect(result freeipmi.Result, ch chan<- prometheus.Metric, target ipmiTarget) (int, error) { currentPowerConsumption, err := freeipmi.GetCurrentPowerConsumption(result) if err != nil { log.Errorf("Failed to collect DCMI data from %s: %s", targetName(target.host), err) return 0, err } ch <- prometheus.MustNewConstMetric( powerConsumptionDesc, prometheus.GaugeValue, currentPowerConsumption, ) return 1, nil } prometheus-ipmi-exporter-1.4.0+ds/collector_ipmi.go000066400000000000000000000123431415415251200224510ustar00rootroot00000000000000package main import ( "math" "strconv" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/soundcloud/ipmi_exporter/freeipmi" ) const ( IPMICollectorName CollectorName = "ipmi" ) var ( sensorStateDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "sensor", "state"), "Indicates the severity of the state reported by an IPMI sensor (0=nominal, 1=warning, 2=critical).", []string{"id", "name", "type"}, nil, ) sensorValueDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "sensor", "value"), "Generic data read from an IPMI sensor of unknown type, relying on labels for context.", []string{"id", "name", "type"}, nil, ) fanSpeedDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "fan_speed", "rpm"), "Fan speed in rotations per minute.", []string{"id", "name"}, nil, ) fanSpeedStateDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "fan_speed", "state"), "Reported state of a fan speed sensor (0=nominal, 1=warning, 2=critical).", []string{"id", "name"}, nil, ) temperatureDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "temperature", "celsius"), "Temperature reading in degree Celsius.", []string{"id", "name"}, nil, ) temperatureStateDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "temperature", "state"), "Reported state of a temperature sensor (0=nominal, 1=warning, 2=critical).", []string{"id", "name"}, nil, ) voltageDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "voltage", "volts"), "Voltage reading in Volts.", []string{"id", "name"}, nil, ) voltageStateDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "voltage", "state"), "Reported state of a voltage sensor (0=nominal, 1=warning, 2=critical).", []string{"id", "name"}, nil, ) currentDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "current", "amperes"), "Current reading in Amperes.", []string{"id", "name"}, nil, ) currentStateDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "current", "state"), "Reported state of a current sensor (0=nominal, 1=warning, 2=critical).", []string{"id", "name"}, nil, ) powerDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "power", "watts"), "Power reading in Watts.", []string{"id", "name"}, nil, ) powerStateDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "power", "state"), "Reported state of a power sensor (0=nominal, 1=warning, 2=critical).", []string{"id", "name"}, nil, ) ) type IPMICollector struct{} func (c IPMICollector) Name() CollectorName { return IPMICollectorName } func (c IPMICollector) Cmd() string { return "ipmimonitoring" } func (c IPMICollector) Args() []string { return []string{ "-Q", "--ignore-unrecognized-events", "--comma-separated-output", "--no-header-output", "--sdr-cache-recreate", "--output-event-bitmask", } } func (c IPMICollector) Collect(result freeipmi.Result, ch chan<- prometheus.Metric, target ipmiTarget) (int, error) { excludeIds := target.config.ExcludeSensorIDs results, err := freeipmi.GetSensorData(result, excludeIds) if err != nil { log.Errorf("Failed to collect sensor data from %s: %s", targetName(target.host), err) return 0, err } for _, data := range results { var state float64 switch data.State { case "Nominal": state = 0 case "Warning": state = 1 case "Critical": state = 2 case "N/A": state = math.NaN() default: log.Errorf("Unknown sensor state: '%s'\n", data.State) state = math.NaN() } log.Debugf("Got values: %v\n", data) switch data.Unit { case "RPM": collectTypedSensor(ch, fanSpeedDesc, fanSpeedStateDesc, state, data) case "C": collectTypedSensor(ch, temperatureDesc, temperatureStateDesc, state, data) case "A": collectTypedSensor(ch, currentDesc, currentStateDesc, state, data) case "V": collectTypedSensor(ch, voltageDesc, voltageStateDesc, state, data) case "W": collectTypedSensor(ch, powerDesc, powerStateDesc, state, data) default: collectGenericSensor(ch, state, data) } } return 1, nil } func (c IPMICollector) Describe(ch chan<- *prometheus.Desc) { ch <- sensorStateDesc ch <- sensorValueDesc ch <- fanSpeedDesc ch <- fanSpeedStateDesc ch <- temperatureDesc ch <- temperatureStateDesc ch <- voltageDesc ch <- voltageStateDesc ch <- currentDesc ch <- currentStateDesc ch <- powerDesc ch <- powerStateDesc } func collectTypedSensor(ch chan<- prometheus.Metric, desc, stateDesc *prometheus.Desc, state float64, data freeipmi.SensorData) { ch <- prometheus.MustNewConstMetric( desc, prometheus.GaugeValue, data.Value, strconv.FormatInt(data.ID, 10), data.Name, ) ch <- prometheus.MustNewConstMetric( stateDesc, prometheus.GaugeValue, state, strconv.FormatInt(data.ID, 10), data.Name, ) } func collectGenericSensor(ch chan<- prometheus.Metric, state float64, data freeipmi.SensorData) { ch <- prometheus.MustNewConstMetric( sensorValueDesc, prometheus.GaugeValue, data.Value, strconv.FormatInt(data.ID, 10), data.Name, data.Type, ) ch <- prometheus.MustNewConstMetric( sensorStateDesc, prometheus.GaugeValue, state, strconv.FormatInt(data.ID, 10), data.Name, data.Type, ) } prometheus-ipmi-exporter-1.4.0+ds/collector_sel.go000066400000000000000000000027411415415251200222770ustar00rootroot00000000000000package main import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/soundcloud/ipmi_exporter/freeipmi" ) const ( SELCollectorName CollectorName = "sel" ) var ( selEntriesCountDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "sel", "logs_count"), "Current number of log entries in the SEL.", []string{}, nil, ) selFreeSpaceDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "sel", "free_space_bytes"), "Current free space remaining for new SEL entries.", []string{}, nil, ) ) type SELCollector struct{} func (c SELCollector) Name() CollectorName { return SELCollectorName } func (c SELCollector) Cmd() string { return "ipmi-sel" } func (c SELCollector) Args() []string { return []string{"--info"} } func (c SELCollector) Collect(result freeipmi.Result, ch chan<- prometheus.Metric, target ipmiTarget) (int, error) { entriesCount, err := freeipmi.GetSELInfoEntriesCount(result) if err != nil { log.Errorf("Failed to collect SEL data from %s: %s", targetName(target.host), err) return 0, err } freeSpace, err := freeipmi.GetSELInfoFreeSpace(result) if err != nil { log.Errorf("Failed to collect SEL data from %s: %s", targetName(target.host), err) return 0, err } ch <- prometheus.MustNewConstMetric( selEntriesCountDesc, prometheus.GaugeValue, entriesCount, ) ch <- prometheus.MustNewConstMetric( selFreeSpaceDesc, prometheus.GaugeValue, freeSpace, ) return 1, nil } prometheus-ipmi-exporter-1.4.0+ds/collector_sm_lan_mode.go000066400000000000000000000030731415415251200237700ustar00rootroot00000000000000package main import ( "fmt" "strconv" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/soundcloud/ipmi_exporter/freeipmi" ) const ( SMLANModeCollectorName CollectorName = "sm-lan-mode" ) var ( lanModeDesc = prometheus.NewDesc( prometheus.BuildFQName(namespace, "config", "lan_mode"), "Returns configured LAN mode (0=dedicated, 1=shared, 2=failover).", nil, nil, ) ) type SMLANModeCollector struct{} func (c SMLANModeCollector) Name() CollectorName { return SMLANModeCollectorName } func (c SMLANModeCollector) Cmd() string { return "ipmi-raw" } func (c SMLANModeCollector) Args() []string { return []string{"0x0", "0x30", "0x70", "0x0c", "0"} } func (c SMLANModeCollector) Collect(result freeipmi.Result, ch chan<- prometheus.Metric, target ipmiTarget) (int, error) { octets, err := freeipmi.GetRawOctets(result) if err != nil { log.Errorf("Failed to collect LAN mode data from %s: %s", targetName(target.host), err) return 0, err } if len(octets) != 3 { log.Errorf("Unexpected number of octets from %s: %+v", targetName(target.host), octets) return 0, fmt.Errorf("unexpected number of octects in raw response: %d", len(octets)) } switch octets[2] { case "00", "01", "02": value, _ := strconv.Atoi(octets[2]) ch <- prometheus.MustNewConstMetric(lanModeDesc, prometheus.GaugeValue, float64(value)) default: log.Errorf("Unexpected lan mode status (ipmi-raw) from %s: %+v", targetName(target.host), octets[2]) return 0, fmt.Errorf("unexpected lan mode status: %s", octets[2]) } return 1, nil } prometheus-ipmi-exporter-1.4.0+ds/config.go000066400000000000000000000160521415415251200207130ustar00rootroot00000000000000package main import ( "fmt" "io/ioutil" "strings" "sync" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/log" "github.com/soundcloud/ipmi_exporter/freeipmi" yaml "gopkg.in/yaml.v2" ) // CollectorName is used for unmarshaling the list of collectors in the yaml config file type CollectorName string // ConfiguredCollector wraps an existing collector implementation, // potentially altering its default settings. type ConfiguredCollector struct { collector collector command string default_args []string custom_args []string } func (c ConfiguredCollector) Name() CollectorName { return c.collector.Name() } func (c ConfiguredCollector) Cmd() string { if c.command != "" { return c.command } return c.collector.Cmd() } func (c ConfiguredCollector) Args() []string { args := []string{} if c.custom_args != nil { // custom args come first, this way it is quite easy to // override a collector to use e.g. sudo args = append(args, c.custom_args...) } if c.default_args != nil { args = append(args, c.default_args...) } else { args = append(args, c.collector.Args()...) } return args } func (c ConfiguredCollector) Collect(output freeipmi.Result, ch chan<- prometheus.Metric, target ipmiTarget) (int, error) { return c.collector.Collect(output, ch, target) } func (c CollectorName) GetInstance() (collector, error) { // This is where a new collector would have to be "registered" switch c { case IPMICollectorName: return IPMICollector{}, nil case BMCCollectorName: return BMCCollector{}, nil case SELCollectorName: return SELCollector{}, nil case DCMICollectorName: return DCMICollector{}, nil case ChassisCollectorName: return ChassisCollector{}, nil case SMLANModeCollectorName: return SMLANModeCollector{}, nil } return nil, fmt.Errorf("invalid collector: %s", string(c)) } func (c CollectorName) IsValid() error { _, err := c.GetInstance() return err } // Config is the Go representation of the yaml config file. type Config struct { Modules map[string]IPMIConfig `yaml:"modules"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // SafeConfig wraps Config for concurrency-safe operations. type SafeConfig struct { sync.RWMutex C *Config } // IPMIConfig is the Go representation of a module configuration in the yaml // config file. type IPMIConfig struct { User string `yaml:"user"` Password string `yaml:"pass"` Privilege string `yaml:"privilege"` Driver string `yaml:"driver"` Timeout uint32 `yaml:"timeout"` Collectors []CollectorName `yaml:"collectors"` ExcludeSensorIDs []int64 `yaml:"exclude_sensor_ids"` WorkaroundFlags []string `yaml:"workaround_flags"` CollectorCmd map[CollectorName]string `yaml:"collector_cmd"` CollectorArgs map[CollectorName][]string `yaml:"default_args"` CustomArgs map[CollectorName][]string `yaml:"custom_args"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } type Args struct { Args map[string][]string } var defaultConfig = IPMIConfig{ Collectors: []CollectorName{IPMICollectorName, DCMICollectorName, BMCCollectorName, ChassisCollectorName}, } func checkOverflow(m map[string]interface{}, ctx string) error { if len(m) > 0 { var keys []string for k := range m { keys = append(keys, k) } return fmt.Errorf("unknown fields in %s: %s", ctx, strings.Join(keys, ", ")) } return nil } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (s *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain Config if err := unmarshal((*plain)(s)); err != nil { return err } if err := checkOverflow(s.XXX, "config"); err != nil { return err } return nil } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (s *IPMIConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *s = defaultConfig type plain IPMIConfig if err := unmarshal((*plain)(s)); err != nil { return err } if err := checkOverflow(s.XXX, "modules"); err != nil { return err } for _, c := range s.Collectors { if err := c.IsValid(); err != nil { return err } } return nil } func (c IPMIConfig) GetCollectors() []collector { result := []collector{} for _, co := range c.Collectors { // At this point validity has already been checked i, _ := co.GetInstance() cc := ConfiguredCollector{ collector: i, command: c.CollectorCmd[i.Name()], default_args: c.CollectorArgs[i.Name()], custom_args: c.CustomArgs[i.Name()], } result = append(result, cc) } return result } func (c IPMIConfig) GetFreeipmiConfig() string { var b strings.Builder if c.Driver != "" { fmt.Fprintf(&b, "driver-type %s\n", c.Driver) } if c.Privilege != "" { fmt.Fprintf(&b, "privilege-level %s\n", c.Privilege) } if c.User != "" { fmt.Fprintf(&b, "username %s\n", c.User) } if c.Password != "" { fmt.Fprintf(&b, "password %s\n", freeipmi.EscapePassword(c.Password)) } if c.Timeout != 0 { fmt.Fprintf(&b, "session-timeout %d\n", c.Timeout) } if len(c.WorkaroundFlags) > 0 { fmt.Fprintf(&b, "workaround-flags") for _, flag := range c.WorkaroundFlags { fmt.Fprintf(&b, " %s", flag) } fmt.Fprintln(&b) } return b.String() } // ReloadConfig reloads the config in a concurrency-safe way. If the configFile // is unreadable or unparsable, an error is returned and the old config is kept. func (sc *SafeConfig) ReloadConfig(configFile string) error { var c = &Config{} var config []byte var err error if configFile != "" { config, err = ioutil.ReadFile(configFile) if err != nil { log.Errorf("Error reading config file: %s", err) return err } } else { config = []byte("# use empty file as default") } if err = yaml.Unmarshal(config, c); err != nil { return err } sc.Lock() sc.C = c sc.Unlock() if configFile != "" { log.Infoln("Loaded config file", configFile) } return nil } // HasModule returns true if a given module is configured. It is concurrency-safe. func (sc *SafeConfig) HasModule(module string) bool { sc.Lock() defer sc.Unlock() _, ok := sc.C.Modules[module] return ok } // ConfigForTarget returns the config for a given target/module, or the // default. It is concurrency-safe. func (sc *SafeConfig) ConfigForTarget(target, module string) IPMIConfig { sc.Lock() defer sc.Unlock() var config IPMIConfig var ok = false if module != "default" { config, ok = sc.C.Modules[module] if !ok { log.Errorf("Requested module %s for target %s not found, using default", module, targetName(target)) } } // If nothing found, fall back to defaults if !ok { config, ok = sc.C.Modules["default"] if !ok { // This is probably fine for running locally, so not making this a warning log.Debugf("Needed default config for target %s, but none configured, using FreeIPMI defaults", targetName(target)) config = defaultConfig } } return config } prometheus-ipmi-exporter-1.4.0+ds/docker-compose.yml000066400000000000000000000005261415415251200225530ustar00rootroot00000000000000version: '3.7' services: ipmi_exporter: build: context: . volumes: - ./ipmi_remote.yml:/config.yml:ro # replace with your own config ports: - 9290:9290 # bind on 0.0.0.0 # - 127.0.0.1:9290:9290 # or bind to specific interface hostname: ipmi_exporter_docker prometheus-ipmi-exporter-1.4.0+ds/docs/000077500000000000000000000000001415415251200200435ustar00rootroot00000000000000prometheus-ipmi-exporter-1.4.0+ds/docs/configuration.md000066400000000000000000000114051415415251200232350ustar00rootroot00000000000000# Configuration Simply scraping the standard `/metrics` endpoint will make the exporter emit local IPMI metrics. No special configuration is required. For remote metrics, the general configuration pattern is similar to that of the [blackbox exporter](https://github.com/prometheus/blackbox_exporter), i.e. Prometheus scrapes a small number (possibly one) of IPMI exporters with a `target` and `module` URL parameter to tell the exporter which IPMI device it should use to retrieve the IPMI metrics. We offer this approach as IPMI devices often provide useful information even while the supervised host is turned off. If you are running the exporter on a separate host anyway, it makes more sense to have only a few of them, each probing many (possibly thousands of) IPMI devices, rather than one exporter per IPMI device. **NOTE:** If you are using remote metrics, but still want to get the local process metrics from the instance, you must use a `default` module with an empty collectors list and use other modules for the remote hosts. ## IPMI exporter The exporter can read a configuration file by setting `config.file` (see above). To collect local metrics, you might not even need one. For remote metrics, it must contain at least user names and passwords for IPMI access to all targets to be scraped. You can additionally specify the IPMI driver type and privilege level to use (see `man 5 freeipmi.conf` for more details and possible values). The config file supports the notion of "modules", so that different configurations can be re-used for groups of targets. See the section below on how to set the module parameter in Prometheus. The special module "default" is used in case the scrape does not request a specific module. The configuration file also supports a blacklist of sensors, useful in case of OEM-specific sensors that FreeIPMI cannot deal with properly or otherwise misbehaving sensors. This applies to both local and remote metrics. There are two commented example configuration files, see `ipmi_local.yml` for scraping local host metrics and `ipmi_remote.yml` for scraping remote IPMI interfaces. ## Prometheus ### Local metrics Collecting local IPMI metrics is fairly straightforward. Simply configure your server to scrape the default metrics endpoint on the hosts running the exporter. ``` - job_name: ipmi scrape_interval: 1m scrape_timeout: 30s metrics_path: /metrics scheme: http static_configs: - targets: - 10.1.2.23:9290 - 10.1.2.24:9290 - 10.1.2.25:9290 ``` ### Remote metrics To add your IPMI targets to Prometheus, you can use any of the supported service discovery mechanism of your choice. The following example uses the file-based SD and should be easy to adjust to other scenarios. Create a YAML file that contains a list of targets, e.g.: ``` --- - targets: - 10.1.2.23 - 10.1.2.24 - 10.1.2.25 - 10.1.2.26 - 10.1.2.27 - 10.1.2.28 - 10.1.2.29 - 10.1.2.30 labels: job: ipmi_exporter ``` This file needs to be stored on the Prometheus server host. Assuming that this file is called `/srv/ipmi_exporter/targets.yml`, and the IPMI exporter is running on a host that has the DNS name `ipmi-exporter.internal.example.com`, add the following to your Prometheus config: ``` - job_name: ipmi params: module: default scrape_interval: 1m scrape_timeout: 30s metrics_path: /ipmi scheme: http file_sd_configs: - files: - /srv/ipmi_exporter/targets.yml refresh_interval: 5m relabel_configs: - source_labels: [__address__] separator: ; regex: (.*) target_label: __param_target replacement: ${1} action: replace - source_labels: [__param_target] separator: ; regex: (.*) target_label: instance replacement: ${1} action: replace - separator: ; regex: .* target_label: __address__ replacement: ipmi-exporter.internal.example.com:9290 action: replace ``` This assumes that all hosts use the default module. If you are using modules in the config file, like in the provided `ipmi_remote.yml` example config, you will need to specify on job for each module, using the respective group of targets. In a more extreme case, for example if you are using different passwords on every host, a good approach is to generate an exporter config file that uses the target name as module names, which would allow you to have single job that uses label replace to set the module. Leave out the `params` in the job definition and instead add a relabel rule like this one: ``` - source_labels: [__address__] separator: ; regex: (.*) target_label: __param_module replacement: ${1} action: replace ``` For more information, e.g. how to use mechanisms other than a file to discover the list of hosts to scrape, please refer to the [Prometheus documentation](https://prometheus.io/docs). prometheus-ipmi-exporter-1.4.0+ds/docs/metrics.md000066400000000000000000000146511415415251200220420ustar00rootroot00000000000000# Exported metrics ## Scrape meta data These metrics provide data about the scrape itself: - `ipmi_up{collector=""}` is `1` if the data for this collector could successfully be retrieved from the remote host, `0` otherwise. The following collectors are available and can be enabled or disabled in the config: - `ipmi`: collects IPMI sensor data. If it fails, sensor metrics (see below) will not be available - `dcmi`: collects DCMI data, currently only power consumption. If it fails, power consumption metrics (see below) will not be available - `bmc`: collects BMC details. If it fails, BMC info metrics (see below) will not be available - `chassis`: collects the current chassis power state (on/off). If it fails, the chassis power state metric (see below) will not be available - `sel`: collects system event log (SEL) details. If it fails, SEL metrics (see below) will not be available - `sm-lan-mode`: collects the "LAN mode" setting in the current BMC config. If it fails, the LAN mode metric (see below) will not be available - `ipmi_scrape_duration_seconds` is the amount of time it took to retrieve the data ## BMC info This metric is only provided if the `bmc` collector is enabled. For some basic information, there is a constant metric `ipmi_bmc_info` with value `1` and labels providing the firmware revision and manufacturer as returned from the BMC, and the host system's firmware version (usually the BIOS version). Example: ipmi_bmc_info{firmware_revision="1.66",manufacturer_id="Dell Inc. (674)",system_firmware_version="2.6.1"} 1 **Note:** some systems do not expose the system's firmware version, in which case it will be exported as `"N/A"`. ## Chassis Power State This metric is only provided if the `chassis` collector is enabled. The metric `ipmi_chassis_power_state` shows the current chassis power state of the machine. The value is 1 for power on, and 0 otherwise. ## Power consumption This metric is only provided if the `dcmi` collector is enabled. The metric `ipmi_dcmi_power_consumption_current_watts` can be used to monitor the live power consumption of the machine in Watts. If in doubt, this metric should be used over any of the sensor data (see below), even if their name might suggest that they measure the same thing. This metric has no labels. ## System event log (SEL) info These metrics are only provided if the `sel` collector is enabled (it isn't by default). The metric `ipmi_sel_entries_count` contains the current number of entries in the SEL. It is a gauge, as the SEL can be cleared at any time. This metric has no labels. The metric `ipmi_sel_free_space_bytes` contains the current number of free space for new SEL entries, in bytes. This metric has no labels. ## Supermicro LAN mode setting This metric is only provided if the `sm-lan-mode` collector is enabled (it isn't by default). **NOTE:** This is a vendor-specific collector, it will only work on Supermicro hardware, possibly even only on _some_ Supermicro systems. **NOTE:** Retrieving this setting requires setting `privilege: "admin"` in the config. See e.g. https://www.supermicro.com/support/faqs/faq.cfm?faq=28159 The metric `ipmi_config_lan_mode` contains the value for the current "LAN mode" setting (see link above): `0` for "dedicated", `1` for "shared", and `2` for "failover". ## Sensors These metrics are only provided if the `ipmi` collector is enabled. IPMI sensors in general have one or two distinct pieces of information that are of interest: a value and/or a state. The exporter always exports both, even if the value is NaN or the state non-sensical. This is so one can still always find the metrics to avoid ending up in a situation where one is looking for e.g. the value of a sensor that is in a critical state, but can't find it and assume this to be a problem. The state of a sensor can be one of _nominal_, _warning_, _critical_, or _N/A_, reflected by the metric values `0`, `1`, `2`, and `NaN` respectively. Think of this as a kind of severity. For sensors with known semantics (i.e. units), corresponding specific metrics are exported. For everything else, generic metrics are exported. ### Temperature sensors Temperature sensors measure a temperature in degrees Celsius and their state usually reflects the temperature going above the vendor-recommended value. For each temperature sensor, two metrics are exported (state and value), using the sensor ID and the sensor name as labels. Example: ipmi_temperature_celsius{id="18",name="Inlet Temp"} 24 ipmi_temperature_state{id="18",name="Inlet Temp"} 0 ### Fan speed sensors Fan speed sensors measure fan speed in rotations per minute (RPM) and their state usually reflects the speed being to low, indicating the fan might be broken. For each fan speed sensor, two metrics are exported (state and value), using the sensor ID and the sensor name as labels. Example: ipmi_fan_speed_rpm{id="12",name="Fan1A"} 4560 ipmi_fan_speed_state{id="12",name="Fan1A"} 0 ### Voltage sensors Voltage sensors measure a voltage in Volts. For each voltage sensor, two metrics are exported (state and value), using the sensor ID and the sensor name as labels. Example: ipmi_voltage_state{id="2416",name="12V"} 0 ipmi_voltage_volts{id="2416",name="12V"} 12 ### Current sensors Current sensors measure a current in Amperes. For each current sensor, two metrics are exported (state and value), using the sensor ID and the sensor name as labels. Example: ipmi_current_state{id="83",name="Current 1"} 0 ipmi_current_amperes{id="83",name="Current 1"} 0 ### Power sensors Power sensors measure power in Watts. For each power sensor, two metrics are exported (state and value), using the sensor ID and the sensor name as labels. Example: ipmi_power_state{id="90",name="Pwr Consumption"} 0 ipmi_power_watts{id="90",name="Pwr Consumption"} 70 Note that based on our observations, this may or may not be a reading reflecting the actual live power consumption. We recommend using the more explicit [power consumption metrics](#power_consumption) for this. ### Generic sensors For all sensors that can not be classified, two generic metrics are exported, the state and the value. However, to provide a little more context, the sensor type is added as label (in addition to name and ID). Example: ipmi_sensor_state{id="139",name="Power Cable",type="Cable/Interconnect"} 0 ipmi_sensor_value{id="139",name="Power Cable",type="Cable/Interconnect"} NaN prometheus-ipmi-exporter-1.4.0+ds/freeipmi/000077500000000000000000000000001415415251200207135ustar00rootroot00000000000000prometheus-ipmi-exporter-1.4.0+ds/freeipmi/freeipmi.go000066400000000000000000000170771415415251200230560ustar00rootroot00000000000000package freeipmi import ( "bytes" "crypto/rand" "encoding/csv" "encoding/hex" "fmt" "math" "os" "os/exec" "path/filepath" "regexp" "strconv" "strings" "syscall" ) var ( ipmiDCMICurrentPowerRegex = regexp.MustCompile(`^Current Power\s*:\s*(?P[0-9.]*)\s*Watts.*`) ipmiChassisPowerRegex = regexp.MustCompile(`^System Power\s*:\s(?P.*)`) ipmiSELEntriesRegex = regexp.MustCompile(`^Number of log entries\s*:\s(?P[0-9.]*)`) ipmiSELFreeSpaceRegex = regexp.MustCompile(`^Free space remaining\s*:\s(?P[0-9.]*)\s*bytes.*`) bmcInfoFirmwareRevisionRegex = regexp.MustCompile(`^Firmware Revision\s*:\s*(?P[0-9.]*).*`) bmcInfoSystemFirmwareVersionRegex = regexp.MustCompile(`^System Firmware Version\s*:\s*(?P[0-9.]*).*`) bmcInfoManufacturerIDRegex = regexp.MustCompile(`^Manufacturer ID\s*:\s*(?P.*)`) ) // Result represents the outcome of a call to one of the FreeIPMI tools. // It can be used with other functions in this package to extract data. type Result struct { output []byte err error } // SensorData represents the reading of a single sensor. type SensorData struct { ID int64 Name string Type string State string Value float64 Unit string Event string } // Logger interface is used to enable logging in async functions that cannot return errors. // The main use case is using a prometheus.log logger, which satisfies this interface. type Logger interface { Debugf(string, ...interface{}) Errorf(string, ...interface{}) } // EscapePassword escapes a password so that the result is suitable for usage in a // FreeIPMI config file. func EscapePassword(password string) string { return strings.Replace(password, "#", "\\#", -1) } func pipeName() string { randBytes := make([]byte, 16) rand.Read(randBytes) return filepath.Join(os.TempDir(), "ipmi_exporter-"+hex.EncodeToString(randBytes)) } func contains(s []int64, elm int64) bool { for _, a := range s { if a == elm { return true } } return false } func getValue(ipmiOutput []byte, regex *regexp.Regexp) (string, error) { for _, line := range strings.Split(string(ipmiOutput), "\n") { match := regex.FindStringSubmatch(line) if match == nil { continue } for i, name := range regex.SubexpNames() { if name != "value" { continue } return match[i], nil } } return "", fmt.Errorf("could not find value in output: %s", string(ipmiOutput)) } func freeipmiConfigPipe(config string, logger Logger) (string, error) { content := []byte(config) pipe := pipeName() err := syscall.Mkfifo(pipe, 0600) if err != nil { return "", err } go func(file string, data []byte) { f, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, os.ModeNamedPipe) if err != nil { logger.Errorf("Error opening pipe: %s", err) } if _, err := f.Write(data); err != nil { logger.Errorf("Error writing config to pipe: %s", err) } f.Close() }(pipe, content) return pipe, nil } func Execute(cmd string, args []string, config string, target string, logger Logger) Result { pipe, err := freeipmiConfigPipe(config, logger) if err != nil { return Result{nil, err} } defer func() { if err := os.Remove(pipe); err != nil { logger.Errorf("Error deleting named pipe: %s", err) } }() args = append(args, "--config-file", pipe) if target != "" { args = append(args, "-h", target) } else { target = "[local]" } logger.Debugf("Executing %s %v", cmd, args) out, err := exec.Command(cmd, args...).CombinedOutput() if err != nil { err = fmt.Errorf("error running %s: %s", cmd, err) } return Result{out, err} } func GetSensorData(ipmiOutput Result, excludeSensorIds []int64) ([]SensorData, error) { var result []SensorData if ipmiOutput.err != nil { return result, fmt.Errorf("%s: %s", ipmiOutput.err, ipmiOutput.output) } r := csv.NewReader(bytes.NewReader(ipmiOutput.output)) fields, err := r.ReadAll() if err != nil { return result, err } for _, line := range fields { var data SensorData data.ID, err = strconv.ParseInt(line[0], 10, 64) if err != nil { return result, err } if contains(excludeSensorIds, data.ID) { continue } data.Name = line[1] data.Type = line[2] data.State = line[3] value := line[4] if value != "N/A" { data.Value, err = strconv.ParseFloat(value, 64) if err != nil { return result, err } } else { data.Value = math.NaN() } data.Unit = line[5] data.Event = strings.Trim(line[6], "'") result = append(result, data) } return result, err } func GetCurrentPowerConsumption(ipmiOutput Result) (float64, error) { if ipmiOutput.err != nil { return -1, fmt.Errorf("%s: %s", ipmiOutput.err, ipmiOutput.output) } value, err := getValue(ipmiOutput.output, ipmiDCMICurrentPowerRegex) if err != nil { return -1, err } return strconv.ParseFloat(value, 64) } func GetChassisPowerState(ipmiOutput Result) (float64, error) { if ipmiOutput.err != nil { return -1, fmt.Errorf("%s: %s", ipmiOutput.err, ipmiOutput.output) } value, err := getValue(ipmiOutput.output, ipmiChassisPowerRegex) if err != nil { return -1, err } if value == "on" { return 1, err } return 0, err } func GetBMCInfoFirmwareRevision(ipmiOutput Result) (string, error) { // Workaround for an issue described here: https://github.com/soundcloud/ipmi_exporter/issues/57 // The command may fail, but produce usable output (minus the system firmware revision). // Try to recover gracefully from that situation by first trying to parse the output, and only // raise the initial error if that also fails. value, err := getValue(ipmiOutput.output, bmcInfoFirmwareRevisionRegex) if err != nil { if ipmiOutput.err != nil { return "", fmt.Errorf("%s: %s", ipmiOutput.err, ipmiOutput.output) } } return value, err } func GetBMCInfoManufacturerID(ipmiOutput Result) (string, error) { // Workaround for an issue described here: https://github.com/soundcloud/ipmi_exporter/issues/57 // The command may fail, but produce usable output (minus the system firmware revision). // Try to recover gracefully from that situation by first trying to parse the output, and only // raise the initial error if that also fails. value, err := getValue(ipmiOutput.output, bmcInfoManufacturerIDRegex) if err != nil { if ipmiOutput.err != nil { return "", fmt.Errorf("%s: %s", ipmiOutput.err, ipmiOutput.output) } } return value, err } func GetBMCInfoSystemFirmwareVersion(ipmiOutput Result) (string, error) { if ipmiOutput.err != nil { return "", fmt.Errorf("%s: %s", ipmiOutput.err, ipmiOutput.output) } return getValue(ipmiOutput.output, bmcInfoSystemFirmwareVersionRegex) } func GetSELInfoEntriesCount(ipmiOutput Result) (float64, error) { if ipmiOutput.err != nil { return -1, fmt.Errorf("%s: %s", ipmiOutput.err, ipmiOutput.output) } value, err := getValue(ipmiOutput.output, ipmiSELEntriesRegex) if err != nil { return -1, err } return strconv.ParseFloat(value, 64) } func GetSELInfoFreeSpace(ipmiOutput Result) (float64, error) { if ipmiOutput.err != nil { return -1, fmt.Errorf("%s: %s", ipmiOutput.err, ipmiOutput.output) } value, err := getValue(ipmiOutput.output, ipmiSELFreeSpaceRegex) if err != nil { return -1, err } return strconv.ParseFloat(value, 64) } func GetRawOctets(ipmiOutput Result) ([]string, error) { if ipmiOutput.err != nil { return nil, fmt.Errorf("%s: %s", ipmiOutput.err, ipmiOutput.output) } strOutput := strings.Trim(string(ipmiOutput.output), " \r\n") if !strings.HasPrefix(strOutput, "rcvd: ") { return nil, fmt.Errorf("unexpected raw response: %s", strOutput) } octects := strings.Split(strOutput[6:], " ") return octects, nil } prometheus-ipmi-exporter-1.4.0+ds/go.mod000066400000000000000000000007501415415251200202230ustar00rootroot00000000000000module github.com/soundcloud/ipmi_exporter go 1.12 require ( github.com/kr/text v0.2.0 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/prometheus/client_golang v1.10.0 github.com/prometheus/common v0.24.0 github.com/stretchr/testify v1.5.1 // indirect google.golang.org/protobuf v1.25.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v2 v2.4.0 ) prometheus-ipmi-exporter-1.4.0+ds/go.sum000066400000000000000000001232311415415251200202500ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= 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= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 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.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= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/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= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/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= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg= github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.24.0 h1:aIycr3wRFxPUq8XlLQlGQ9aNXV3dFi5y62pe/SB262k= github.com/prometheus/common v0.24.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 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= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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= 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= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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/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= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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/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= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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/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= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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/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= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-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/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/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= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 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= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= prometheus-ipmi-exporter-1.4.0+ds/ipmi_local.yml000066400000000000000000000011241415415251200217440ustar00rootroot00000000000000# Configuration file for ipmi_exporter # This is an example config for scraping the local host. # In most cases, this should work without using a config file at all. modules: default: # Available collectors are bmc, ipmi, chassis, dcmi, sel, and sm-lan-mode collectors: - bmc - ipmi - dcmi - chassis - sel # Got any sensors you don't care about? Add them here. exclude_sensor_ids: - 2 - 29 - 32 prometheus-ipmi-exporter-1.4.0+ds/ipmi_remote.yml000066400000000000000000000075341415415251200221600ustar00rootroot00000000000000# Configuration file for ipmi_exporter # This is an example config for scraping remote hosts via IPMI. # Information required to access remote IPMI interfaces can be supplied in the # 'modules' section. A scrape can request the usage of a given config by # setting the `module` URL parameter. modules: default: # These settings are used if no module is specified, the # specified module doesn't exist, or of course if # module=default is specified. user: "default_user" pass: "example_pw" # The below settings correspond to driver-type, privilege-level, and # session-timeout respectively, see `man 5 freeipmi.conf` (and e.g. # `man 8 ipmi-sensors` for a list of driver types). driver: "LAN_2_0" privilege: "user" # The session timeout is in milliseconds. Note that a scrape can take up # to (session-timeout * #-of-collectors) milliseconds, so set the scrape # timeout in Prometheus accordingly. timeout: 10000 # Available collectors are bmc, ipmi, chassis, dcmi, sel, and sm-lan-mode # If _not_ specified, bmc, ipmi, chassis, and dcmi are used collectors: - bmc - ipmi - chassis # Got any sensors you don't care about? Add them here. exclude_sensor_ids: - 2 - 29 - 32 - 50 - 52 - 55 dcmi: # Use these settings when scraped with module=dcmi. user: "admin_user" pass: "another_pw" privilege: "admin" driver: "LAN_2_0" collectors: - dcmi thatspecialhost: # Use these settings when scraped with module=thatspecialhost. user: "some_user" pass: "secret_pw" privilege: "admin" driver: "LAN" collectors: - ipmi - sel # Need any special workaround flags set? Add them here. # Workaround flags might be needed to address issues with specific vendor implementations # e.g. https://www.gnu.org/software/freeipmi/freeipmi-faq.html#Why-is-the-output-from-FreeIPMI-different-than-another-software_003f # For a full list of flags, refer to: # https://www.gnu.org/software/freeipmi/manpages/man8/ipmi-sensors.8.html#lbAL workaround_flags: - discretereading # If you require additional command line arguments (e.g. --bridge-sensors for ipmimonitoring), # you can specify them per collector - BE CAREFUL, you can easily break the exporter with this! custom_args: ipmi: - "--bridge-sensors" advanced: # Use these settings when scraped with module=advanced. user: "some_user" pass: "secret_pw" privilege: "admin" driver: "LAN" collectors: - ipmi - sel # USING ANY OF THE BELOW VOIDS YOUR WARRANTY! YOU MAY GET BITTEN BY SHARKS! # You can override the command to be executed for a collector. Paired with # custom_args, this can be used to e.g. execute the IPMI tools with sudo: collector_cmd: ipmi: sudo sel: sudo custom_args: ipmi: - "ipmimonitoring" sel: - "ipmi-sel" prometheus-ipmi-exporter-1.4.0+ds/main.go000066400000000000000000000100531415415251200203650ustar00rootroot00000000000000package main import ( "fmt" "net/http" "os" "os/signal" "syscall" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/log" "github.com/prometheus/common/version" kingpin "gopkg.in/alecthomas/kingpin.v2" ) var ( configFile = kingpin.Flag( "config.file", "Path to configuration file.", ).String() executablesPath = kingpin.Flag( "freeipmi.path", "Path to FreeIPMI executables (default: rely on $PATH).", ).String() listenAddress = kingpin.Flag( "web.listen-address", "Address to listen on for web interface and telemetry.", ).Default(":9290").String() sc = &SafeConfig{ C: &Config{}, } reloadCh chan chan error ) func remoteIPMIHandler(w http.ResponseWriter, r *http.Request) { target := r.URL.Query().Get("target") if target == "" { http.Error(w, "'target' parameter must be specified", 400) return } // Remote scrape will not work without some kind of config, so be pedantic about it module := r.URL.Query().Get("module") if module == "" { module = "default" } if !sc.HasModule(module) { http.Error(w, fmt.Sprintf("Unknown module %q", module), http.StatusBadRequest) return } log.Debugf("Scraping target '%s' with module '%s'", target, module) registry := prometheus.NewRegistry() remoteCollector := metaCollector{target: target, module: module, config: sc} registry.MustRegister(remoteCollector) h := promhttp.HandlerFor(registry, promhttp.HandlerOpts{}) h.ServeHTTP(w, r) } func updateConfiguration(w http.ResponseWriter, r *http.Request) { switch r.Method { case "POST": rc := make(chan error) reloadCh <- rc if err := <-rc; err != nil { http.Error(w, fmt.Sprintf("failed to reload config: %s", err), http.StatusInternalServerError) } default: log.Errorf("Only POST requests allowed for %s", r.URL) w.Header().Set("Allow", "POST") http.Error(w, "Only POST requests allowed", http.StatusMethodNotAllowed) } } func main() { log.AddFlags(kingpin.CommandLine) kingpin.HelpFlag.Short('h') kingpin.Version(version.Print("ipmi_exporter")) kingpin.Parse() log.Infoln("Starting ipmi_exporter") // Bail early if the config is bad. if err := sc.ReloadConfig(*configFile); err != nil { log.Fatalf("Error parsing config file: %s", err) } hup := make(chan os.Signal) reloadCh = make(chan chan error) signal.Notify(hup, syscall.SIGHUP) go func() { for { select { case <-hup: if err := sc.ReloadConfig(*configFile); err != nil { log.Errorf("Error reloading config: %s", err) } case rc := <-reloadCh: if err := sc.ReloadConfig(*configFile); err != nil { log.Errorf("Error reloading config: %s", err) rc <- err } else { rc <- nil } } } }() localCollector := metaCollector{target: targetLocal, module: "default", config: sc} prometheus.MustRegister(&localCollector) http.Handle("/metrics", promhttp.Handler()) // Regular metrics endpoint for local IPMI metrics. http.HandleFunc("/ipmi", remoteIPMIHandler) // Endpoint to do IPMI scrapes. http.HandleFunc("/-/reload", updateConfiguration) // Endpoint to reload configuration. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(` IPMI Exporter

IPMI Exporter


Local metrics

Config

`)) }) log.Infof("Listening on %s", *listenAddress) err := http.ListenAndServe(*listenAddress, nil) if err != nil { log.Fatal(err) } }