pax_global_header00006660000000000000000000000064144535333310014516gustar00rootroot0000000000000052 comment=c0848dc8c58f2a59b75396b178c584096eb54b0c prometheus-smokeping-prober-0.7.1/000077500000000000000000000000001445353333100171775ustar00rootroot00000000000000prometheus-smokeping-prober-0.7.1/.circleci/000077500000000000000000000000001445353333100210325ustar00rootroot00000000000000prometheus-smokeping-prober-0.7.1/.circleci/config.yml000066400000000000000000000030211445353333100230160ustar00rootroot00000000000000--- version: 2.1 orbs: prometheus: prometheus/prometheus@0.17.1 executors: # This must match .promu.yml. golang: docker: - image: cimg/go:1.20 jobs: test: executor: golang steps: - checkout - run: go mod download - run: make promu - run: make style lint unused test build - run: rm -v smokeping_prober workflows: version: 2 smokeping_prober: jobs: - test: filters: tags: only: /.*/ - prometheus/build: name: build parallelism: 3 promu_opts: "-p linux/amd64 -p windows/amd64 -p linux/arm64 -p darwin/amd64 -p darwin/arm64 -p linux/386" filters: tags: ignore: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/ branches: ignore: /^(master|release-.*|.*build-all.*)$/ - prometheus/build: name: build_all parallelism: 3 filters: branches: only: /^(master|release-.*|.*build-all.*)$/ tags: only: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/ - prometheus/publish_master: docker_hub_organization: superque quay_io_organization: superq requires: - test - build_all filters: branches: only: master - prometheus/publish_release: docker_hub_organization: superque quay_io_organization: superq requires: - test - build_all filters: tags: only: /^v.*/ branches: ignore: /.*/ prometheus-smokeping-prober-0.7.1/.github/000077500000000000000000000000001445353333100205375ustar00rootroot00000000000000prometheus-smokeping-prober-0.7.1/.github/dependabot.yml000066400000000000000000000001561445353333100233710ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "gomod" directory: "/" schedule: interval: "monthly" prometheus-smokeping-prober-0.7.1/.github/workflows/000077500000000000000000000000001445353333100225745ustar00rootroot00000000000000prometheus-smokeping-prober-0.7.1/.github/workflows/golangci-lint.yml000066400000000000000000000013731445353333100260520ustar00rootroot00000000000000name: golangci-lint on: push: paths: - "go.sum" - "go.mod" - "**.go" - "scripts/errcheck_excludes.txt" - ".github/workflows/golangci-lint.yml" - ".golangci.yml" pull_request: jobs: golangci: name: lint runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v3 - name: install Go uses: actions/setup-go@v3 with: go-version: 1.20.x - name: Install snmp_exporter/generator dependencies run: sudo apt-get update && sudo apt-get -y install libsnmp-dev if: github.repository == 'prometheus/snmp_exporter' - name: Lint uses: golangci/golangci-lint-action@v3.4.0 with: version: v1.53.3 prometheus-smokeping-prober-0.7.1/.gitignore000066400000000000000000000000751445353333100211710ustar00rootroot00000000000000smokeping_prober /.build /.deps /.release /.tarballs /vendor prometheus-smokeping-prober-0.7.1/.golangci.yml000066400000000000000000000012721445353333100215650ustar00rootroot00000000000000linters: enable: - misspell - revive disable: # Disable soon to deprecated[1] linters that lead to false # positives when build tags disable certain files[2] # 1: https://github.com/golangci/golangci-lint/issues/1841 # 2: https://github.com/prometheus/node_exporter/issues/1545 - deadcode - unused - structcheck - varcheck issues: exclude-rules: - path: _test.go linters: - errcheck linters-settings: errcheck: exclude-functions: # Used in HTTP handlers, any error is handled by the server itself. - (net/http.ResponseWriter).Write # Never check for logger errors. - (github.com/go-kit/log.Logger).Log prometheus-smokeping-prober-0.7.1/.promu.yml000066400000000000000000000010531445353333100211410ustar00rootroot00000000000000go: # This must match .circle/config.yml. version: 1.20 repository: path: github.com/superq/smokeping_prober build: flags: -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-smokeping-prober-0.7.1/CHANGELOG.md000066400000000000000000000025061445353333100210130ustar00rootroot00000000000000## master / unreleased * [CHANGE] * [FEATURE] * [ENHANCEMENT] * [BUGFIX] ## 0.7.1 / 2023-06-12 * [BUGFIX] Ignore network read timeout errors #115 ## 0.7.0 / 2023-07-10 * [FEATURE] Make source ip configurable #83 * [FEATURE] Add metrics for send and receive errors #109 * [FEATURE] Enable native histograms #108 ## 0.6.1 / 2022-06-08 * [BUGFIX] Fix RecordRtts memory leak for config file targets #73 ## 0.6.0 / 2022-05-23 * [CHANGE] Exit with a non-zero error code on fatal error #70 * [FEATURE] Add TLS/Auth from exporter-toolkit #69 ## 0.5.2 / 2022-03-17 Re-tag release. ## 0.5.1 / 2022-03-16 * [BUGFIX] Fix histogram buckets #65 ## 0.5.0 / 2021-12-19 * [FEATURE] Support loading targets from a config file #54 ## 0.4.2 / 2021-04-10 * [BUGFIX] Fix sequence number wrapping #53 ## 0.4.1 / 2021-02-02 * [ENHANCEMENT] Smooth out pingers over the interval #48 * [BUGFIX] Fix memory leak #49 ## 0.4.0 / 2021-01-31 * [FEATURE] Add support for duplicate packet detection #47 ## 0.3.1 / 2020-06-04 Rebuild with latest Go / vendoring. ## 0.3.0 / 2019-06-25 * [FEATURE] Enable pprof * [FEATURE] Add TTL monitoring #20 * [ENHANCEMENT] Update to latest upstream go-ping library #20 ## 0.2.0 / 2019-05-14 * [FEATURE] Add example rules file. #12 * [FEATURE] Allow setting buckets from flag. #17 ## 0.1.0 / 2018-11-08 Initial release prometheus-smokeping-prober-0.7.1/Dockerfile000066400000000000000000000004351445353333100211730ustar00rootroot00000000000000ARG ARCH="amd64" ARG OS="linux" FROM quay.io/prometheus/busybox-${OS}-${ARCH}:latest LABEL maintainer="Ben Kochie " ARG ARCH="amd64" ARG OS="linux" COPY .build/${OS}-${ARCH}/smokeping_prober /bin/smokeping_prober EXPOSE 9374 ENTRYPOINT [ "/bin/smokeping_prober" ] prometheus-smokeping-prober-0.7.1/LICENSE000066400000000000000000000261351445353333100202130ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. prometheus-smokeping-prober-0.7.1/Makefile000066400000000000000000000013171445353333100206410ustar00rootroot00000000000000# 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. DOCKER_ARCHS ?= amd64 armv7 arm64 DOCKER_REPO ?= quay.io/superq DOCKER_IMAGE_NAME ?= smokeping-prober include Makefile.common prometheus-smokeping-prober-0.7.1/Makefile.common000066400000000000000000000216551445353333100221370ustar00rootroot00000000000000# 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])\.') 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 command -v gotestsum > /dev/null),) GOTEST_DIR := test-results GOTEST := gotestsum --junitfile $(GOTEST_DIR)/unit-tests.xml -- endif endif PROMU_VERSION ?= 0.14.0 PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= GOLANGCI_LINT_VERSION ?= v1.53.3 # 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)) # If we're in CI and there is an Actions file, that means the linter # is being run in Actions, so we don't need to run it here. ifneq (,$(SKIP_GOLANGCI_LINT)) GOLANGCI_LINT := else ifeq (,$(CIRCLE_JOB)) GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint else ifeq (,$(wildcard .github/workflows/golangci-lint.yml)) GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint endif 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)) SANITIZED_DOCKER_IMAGE_TAG := $(subst +,-,$(DOCKER_IMAGE_TAG)) 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 yamllint 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" $(GO) mod download .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 -d $$m; \ done $(GO) mod tidy .PHONY: common-test-short common-test-short: $(GOTEST_DIR) @echo ">> running short tests" $(GOTEST) -short $(GOOPTS) $(pkgs) .PHONY: common-test common-test: $(GOTEST_DIR) @echo ">> running all tests" $(GOTEST) $(test-flags) $(GOOPTS) $(pkgs) $(GOTEST_DIR): @mkdir -p $@ .PHONY: common-format common-format: @echo ">> formatting code" $(GO) fmt $(pkgs) .PHONY: common-vet common-vet: @echo ">> vetting code" $(GO) vet $(GOOPTS) $(pkgs) .PHONY: common-lint common-lint: $(GOLANGCI_LINT) ifdef GOLANGCI_LINT @echo ">> running golangci-lint" # 'go list' needs to be executed before staticcheck to prepopulate the modules cache. # Otherwise staticcheck might fail randomly for some reason not yet explained. $(GO) list -e -compiled -test=true -export=false -deps=true -find=false -tags= -- ./... > /dev/null $(GOLANGCI_LINT) run $(GOLANGCI_LINT_OPTS) $(pkgs) endif .PHONY: common-yamllint common-yamllint: @echo ">> running yamllint on all YAML files in the repository" ifeq (, $(shell command -v yamllint > /dev/null)) @echo "yamllint not installed so skipping" else yamllint . endif # For backward-compatibility. .PHONY: common-staticcheck common-staticcheck: lint .PHONY: common-unused common-unused: @echo ">> running check for unused/missing packages in go.mod" $(GO) mod tidy @git diff --exit-code -- go.sum go.mod .PHONY: common-build common-build: promu @echo ">> building binaries" $(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-$*:$(SANITIZED_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-$*:$(SANITIZED_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-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest" docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_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):$(SANITIZED_DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(SANITIZED_DOCKER_IMAGE_TAG)) DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_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 .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-smokeping-prober-0.7.1/README.md000066400000000000000000000103451445353333100204610ustar00rootroot00000000000000# smokeping_prober [![CircleCI](https://circleci.com/gh/SuperQ/smokeping_prober/tree/master.svg?style=svg)](https://circleci.com/gh/SuperQ/smokeping_prober/tree/master) [![Docker Repository on Quay](https://quay.io/repository/superq/smokeping-prober/status "Docker Repository on Quay")](https://quay.io/repository/superq/smokeping-prober) Prometheus style "smokeping" prober. ![Example Graph](example-graph.png) ## Overview This prober sends a series of ICMP (or UDP) pings to a target and records the responses in Prometheus histogram metrics. ``` usage: smokeping_prober [] [...] Flags: -h, --help Show context-sensitive help (also try --help-long and --help-man). --config.file=CONFIG.FILE Optional smokeping_prober configuration yaml file. --web.telemetry-path="/metrics" Path under which to expose metrics. --web.systemd-socket Use systemd socket activation listeners instead of port listeners (Linux only). --web.listen-address=:9374 ... Addresses on which to expose metrics and web interface. Repeatable for multiple addresses. --web.config.file="" [EXPERIMENTAL] Path to configuration file that can enable TLS or authentication. --buckets="5e-05,0.0001,0.0002,0.0004,0.0008,0.0016,0.0032,0.0064,0.0128,0.0256,0.0512,0.1024,0.2048,0.4096,0.8192,1.6384,3.2768,6.5536,13.1072,26.2144" A comma delimited list of buckets to use -i, --ping.interval=1s Ping interval duration --privileged Run in privileged ICMP mode -s, --ping.size=56 Ping packet size in bytes --log.level=info Only log messages with the given severity or above. One of: [debug, info, warn, error] --log.format=logfmt Output format of log messages. One of: [logfmt, json] --version Show application version. Args: [] List of hosts to ping ``` ## Configuration The prober can take a list of targets and parameters from the command line or from a yaml config file. Example config: ```yaml --- targets: - hosts: - host1 - host2 interval: 1s # Duration, Default 1s. network: ip # One of ip, ip4, ip6. Default: ip (automatic IPv4/IPv6) protocol: icmp # One of icmp, udp. Default: icmp (Requires privileged operation) size: 56 # Packet data size in bytes. Default 56 (Range: 24 - 65535) source: 127.0.1.1 # Souce IP address to use. Default: None (automatic selection) ``` In each host group the `interval`, `network`, and `protocol` are optional. The interval Duration is in [Go time.ParseDuration()](https://golang.org/pkg/time/#ParseDuration) syntax. NOTE: The config is only read on startup, SIGHUP is not supported (yet). ## Building and running Requires Go >= 1.17 ```console go install github.com/superq/smokeping_prober@latest sudo setcap cap_net_raw=+ep ${GOPATH}/bin/smokeping_prober ``` ## Docker ```bash docker run \ -p 9374:9374 \ --privileged \ quay.io/superq/smokeping-prober:latest \ some-ping-target.example.com ``` ## Metrics Metric Name | Type | Description ----------------------------------------|------------|------------------------------------------- smokeping\_requests\_total | Counter | Counter of pings sent. smokeping\_response\_duration\_seconds | Histogram | Ping response duration. smokeping\_response\_ttl | Gauge | The last response Time To Live (TTL). smokeping\_response\_duplicates\_total | Counter | The number of duplicated response packets. smokeping\_receive\_errors\_total | Counter | The number of errors when Pinger attempts to receive packets. smokeping\_send\_errors\_total | Counter | The number of errors when Pinger attempts to send packets. ### TLS and basic authentication The Smokeping Prober supports TLS and basic authentication. To use TLS and/or basic authentication, you need to pass a configuration file using the `--web.config.file` parameter. The format of the file is described [in the exporter-toolkit repository](https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md). prometheus-smokeping-prober-0.7.1/VERSION000066400000000000000000000000061445353333100202430ustar00rootroot000000000000000.7.1 prometheus-smokeping-prober-0.7.1/collector.go000066400000000000000000000122761445353333100215240ustar00rootroot00000000000000// Copyright 2018 Ben Kochie // // 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. package main import ( "net" "github.com/prometheus-community/pro-bing" "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" ) const ( namespace = "smokeping" ) var ( labelNames = []string{"ip", "host", "source"} pingResponseTTL = promauto.NewGaugeVec( prometheus.GaugeOpts{ Namespace: namespace, Name: "response_ttl", Help: "The last response Time To Live (TTL).", }, labelNames, ) pingResponseDuplicates = promauto.NewCounterVec( prometheus.CounterOpts{ Namespace: namespace, Name: "response_duplicates_total", Help: "The number of duplicated response packets.", }, labelNames, ) pingRecvErrors = promauto.NewCounter( prometheus.CounterOpts{ Namespace: namespace, Name: "receive_errors_total", Help: "The number of errors when Pinger attempts to receive packets.", }, ) pingSendErrors = promauto.NewCounterVec( prometheus.CounterOpts{ Namespace: namespace, Name: "send_errors_total", Help: "The number of errors when Pinger attempts to send packets.", }, labelNames, ) ) func newPingResponseHistogram(buckets []float64, factor float64) *prometheus.HistogramVec { return prometheus.NewHistogramVec( prometheus.HistogramOpts{ Namespace: namespace, Name: "response_duration_seconds", Help: "A histogram of latencies for ping responses.", Buckets: buckets, NativeHistogramBucketFactor: factor, }, labelNames, ) } // SmokepingCollector collects metrics from the pinger. type SmokepingCollector struct { pingers *[]*probing.Pinger requestsSent *prometheus.Desc } func NewSmokepingCollector(pingers *[]*probing.Pinger, pingResponseSeconds prometheus.HistogramVec) *SmokepingCollector { for _, pinger := range *pingers { // Init all metrics to 0s. ipAddr := pinger.IPAddr().String() pingResponseDuplicates.WithLabelValues(ipAddr, pinger.Addr(), pinger.Source) pingResponseSeconds.WithLabelValues(ipAddr, pinger.Addr(), pinger.Source) pingResponseTTL.WithLabelValues(ipAddr, pinger.Addr(), pinger.Source) pingSendErrors.WithLabelValues(ipAddr, pinger.Addr(), pinger.Source) // Setup handler functions. pinger.OnRecv = func(pkt *probing.Packet) { pingResponseSeconds.WithLabelValues(pkt.IPAddr.String(), pkt.Addr, pinger.Source).Observe(pkt.Rtt.Seconds()) pingResponseTTL.WithLabelValues(pkt.IPAddr.String(), pkt.Addr, pinger.Source).Set(float64(pkt.TTL)) level.Debug(logger).Log("msg", "Echo reply", "ip_addr", pkt.IPAddr, "bytes_received", pkt.Nbytes, "icmp_seq", pkt.Seq, "time", pkt.Rtt, "ttl", pkt.TTL) } pinger.OnDuplicateRecv = func(pkt *probing.Packet) { pingResponseDuplicates.WithLabelValues(pkt.IPAddr.String(), pkt.Addr, pinger.Source).Inc() level.Debug(logger).Log("msg", "Echo reply (DUP!)", "ip_addr", pkt.IPAddr, "bytes_received", pkt.Nbytes, "icmp_seq", pkt.Seq, "time", pkt.Rtt, "ttl", pkt.TTL) } pinger.OnFinish = func(stats *probing.Statistics) { level.Debug(logger).Log("msg", "Ping statistics", "addr", stats.Addr, "packets_sent", stats.PacketsSent, "packets_received", stats.PacketsRecv, "packet_loss_percent", stats.PacketLoss, "min_rtt", stats.MinRtt, "avg_rtt", stats.AvgRtt, "max_rtt", stats.MaxRtt, "stddev_rtt", stats.StdDevRtt) } pinger.OnRecvError = func(err error) { if neterr, ok := err.(*net.OpError); ok { if neterr.Timeout() { // Ignore read timeout errors, these are handled by the pinger. return } } pingRecvErrors.Inc() level.Debug(logger).Log("msg", "Error receiving packet", "error", err) } pinger.OnSendError = func(pkt *probing.Packet, err error) { pingSendErrors.WithLabelValues(pkt.IPAddr.String(), pkt.Addr, pinger.Source).Inc() level.Debug(logger).Log("msg", "Error sending packet", "ip_addr", pkt.IPAddr, "bytes_received", pkt.Nbytes, "icmp_seq", pkt.Seq, "time", pkt.Rtt, "ttl", pkt.TTL, "error", err) } } return &SmokepingCollector{ pingers: pingers, requestsSent: prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "requests_total"), "Number of ping requests sent", labelNames, nil, ), } } func (s *SmokepingCollector) Describe(ch chan<- *prometheus.Desc) { ch <- s.requestsSent } func (s *SmokepingCollector) Collect(ch chan<- prometheus.Metric) { for _, pinger := range *s.pingers { stats := pinger.Statistics() ch <- prometheus.MustNewConstMetric( s.requestsSent, prometheus.CounterValue, float64(stats.PacketsSent), stats.IPAddr.String(), stats.Addr, pinger.Source, ) } } prometheus-smokeping-prober-0.7.1/config/000077500000000000000000000000001445353333100204445ustar00rootroot00000000000000prometheus-smokeping-prober-0.7.1/config/config.go000066400000000000000000000056621445353333100222510ustar00rootroot00000000000000// Copyright 2021 Ben Kochie // // 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. package config import ( "fmt" "os" "sync" "time" yaml "gopkg.in/yaml.v2" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" ) const namespace = "smokeping_prober" var ( configReloadSuccess = promauto.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, Name: "config_last_reload_successful", Help: "Blackbox exporter config loaded successfully.", }) configReloadSeconds = promauto.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, Name: "config_last_reload_success_timestamp_seconds", Help: "Timestamp of the last successful configuration reload.", }) // DefaultTargetGroup sets the default configuration for the TargetGroup DefaultTargetGroup = TargetGroup{ Interval: time.Second, Network: "ip", Protocol: "icmp", Size: 56, } ) type Config struct { Targets []TargetGroup `yaml:"targets"` } type SafeConfig struct { sync.RWMutex C *Config } func (sc *SafeConfig) ReloadConfig(confFile string) (err error) { var c = &Config{} defer func() { if err != nil { configReloadSuccess.Set(0) } else { configReloadSuccess.Set(1) configReloadSeconds.SetToCurrentTime() } }() yamlReader, err := os.Open(confFile) if err != nil { return fmt.Errorf("error reading config file: %w", err) } defer yamlReader.Close() decoder := yaml.NewDecoder(yamlReader) if err = decoder.Decode(c); err != nil { return fmt.Errorf("error parsing config file: %w", err) } sc.Lock() sc.C = c sc.Unlock() return nil } type TargetGroup struct { Hosts []string `yaml:"hosts"` Interval time.Duration `yaml:"interval,omitempty"` Network string `yaml:"network,omitempty"` Protocol string `yaml:"protocol,omitempty"` Size int `yaml:"size,omitempty"` Source string `yaml:"source,omitempty"` // TODO: Needs work to fix MetricFamily consistency. // Labels map[string]string `yaml:"labels,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (s *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain Config return unmarshal((*plain)(s)) } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (s *TargetGroup) UnmarshalYAML(unmarshal func(interface{}) error) error { *s = DefaultTargetGroup type plain TargetGroup return unmarshal((*plain)(s)) } prometheus-smokeping-prober-0.7.1/dashboard.json000066400000000000000000000310631445353333100220240ustar00rootroot00000000000000{ "__inputs": [ { "name": "DS_PROMETHEUS", "label": "Prometheus", "description": "", "type": "datasource", "pluginId": "prometheus", "pluginName": "Prometheus" } ], "__elements": {}, "__requires": [ { "type": "grafana", "id": "grafana", "name": "Grafana", "version": "9.2.4" }, { "type": "panel", "id": "heatmap", "name": "Heatmap", "version": "" }, { "type": "datasource", "id": "prometheus", "name": "Prometheus", "version": "1.0.0" }, { "type": "panel", "id": "timeseries", "name": "Time series", "version": "" } ], "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "datasource", "uid": "grafana" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "target": { "limit": 100, "matchAny": false, "tags": [], "type": "dashboard" }, "type": "dashboard" } ] }, "description": "Smoke Ping using https://github.com/SuperQ/smokeping_prober\r\nwith \r\nlatency heatmap\r\nlatency graph\r\npacket loss gragh\r\n", "editable": true, "fiscalYearStartMonth": 0, "gnetId": 11335, "graphTooltip": 0, "id": null, "links": [], "liveNow": false, "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 7, "panels": [], "repeat": "target", "repeatDirection": "h", "title": "$target", "type": "row" }, { "cards": {}, "color": { "cardColor": "#FF9830", "colorScale": "sqrt", "colorScheme": "interpolateOranges", "exponent": 0.5, "mode": "opacity" }, "dataFormat": "tsbuckets", "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "custom": { "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "scaleDistribution": { "type": "linear" } } }, "overrides": [] }, "gridPos": { "h": 10, "w": 12, "x": 0, "y": 1 }, "heatmap": {}, "hideZeroBuckets": false, "highlightCards": true, "id": 2, "legend": { "show": false }, "links": [], "options": { "calculate": false, "calculation": {}, "cellGap": 2, "cellValues": {}, "color": { "exponent": 0.5, "fill": "#FF9830", "mode": "opacity", "reverse": false, "scale": "exponential", "scheme": "Oranges", "steps": 128 }, "exemplars": { "color": "rgba(255,0,255,0.7)" }, "filterValues": { "le": 1e-9 }, "legend": { "show": false }, "rowsFrame": { "layout": "auto" }, "showValue": "never", "tooltip": { "show": true, "yHistogram": false }, "yAxis": { "axisPlacement": "left", "decimals": 0, "min": "0", "reverse": false, "unit": "s" } }, "pluginVersion": "9.2.4", "reverseYBuckets": false, "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "sum(rate(smokeping_response_duration_seconds_bucket{host=\"${target:raw}\"}[1m])) by (le)", "format": "heatmap", "intervalFactor": 1, "legendFormat": "{{le}}", "range": true, "refId": "A" } ], "title": "Smoke Ping - $target", "tooltip": { "show": true, "showHistogram": false }, "type": "heatmap", "xAxis": { "show": true }, "yAxis": { "decimals": 0, "format": "s", "logBase": 1, "min": "0", "show": true }, "yBucketBound": "auto" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "Loss %", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] }, "unit": "percentunit" }, "overrides": [ { "matcher": { "id": "byName", "options": "Count" }, "properties": [ { "id": "custom.fillOpacity", "value": 0 }, { "id": "unit", "value": "none" }, { "id": "decimals", "value": 0 }, { "id": "custom.axisLabel", "value": "Loss Packet" }, { "id": "custom.lineStyle", "value": { "dash": [ 10, 10 ], "fill": "dash" } } ] } ] }, "gridPos": { "h": 10, "w": 12, "x": 12, "y": 1 }, "id": 4, "options": { "legend": { "calcs": [ "lastNotNull" ], "displayMode": "table", "placement": "bottom", "showLegend": true }, "tooltip": { "mode": "multi", "sort": "none" } }, "pluginVersion": "9.2.4", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "(smokeping_requests_total{host=\"${target:raw}\"} - smokeping_response_duration_seconds_count{host=\"${target:raw}\"})/smokeping_requests_total{host=\"${target:raw}\"} ", "legendFormat": "Percentage", "range": true, "refId": "A" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "(smokeping_requests_total{host=\"${target:raw}\"} - smokeping_response_duration_seconds_count{host=\"${target:raw}\"})", "legendFormat": "Count", "range": true, "refId": "B" } ], "title": "Packet Loss - $target", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 10, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "never", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "links": [], "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [ { "matcher": { "id": "byName", "options": "Count" }, "properties": [ { "id": "custom.fillOpacity", "value": 0 }, { "id": "unit", "value": "none" }, { "id": "decimals", "value": 0 }, { "id": "custom.axisPlacement", "value": "hidden" }, { "id": "custom.axisLabel", "value": "Loss Packet" }, { "id": "custom.lineStyle", "value": { "dash": [ 10, 10 ], "fill": "dash" } } ] } ] }, "gridPos": { "h": 10, "w": 24, "x": 0, "y": 11 }, "id": 5, "options": { "legend": { "calcs": [ "mean", "lastNotNull", "max", "min" ], "displayMode": "table", "placement": "bottom", "showLegend": true }, "tooltip": { "mode": "multi", "sort": "none" } }, "pluginVersion": "9.2.4", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "smokeping_response_duration_seconds_sum{host=\"${target:raw}\"} / smokeping_response_duration_seconds_count{host=\"${target:raw}\"}", "legendFormat": "{{host}}", "range": true, "refId": "A" } ], "title": "Latency - $target", "type": "timeseries" } ], "refresh": "30s", "schemaVersion": 37, "style": "dark", "tags": [], "templating": { "list": [ { "current": {}, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "definition": "label_values(smokeping_requests_total, host)", "hide": 0, "includeAll": true, "multi": false, "name": "target", "options": [], "query": { "query": "label_values(smokeping_requests_total, host)", "refId": "Prometheus-target-Variable-Query" }, "refresh": 1, "regex": "", "skipUrlSync": false, "sort": 1, "tagValuesQuery": "", "tagsQuery": "", "type": "query", "useTags": false } ] }, "time": { "from": "now-1h", "to": "now" }, "timepicker": { "refresh_intervals": [ "5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d" ], "time_options": [ "5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d" ] }, "timezone": "", "title": "Smokeping", "uid": "i5aRaLaik", "version": 12, "weekStart": "" }prometheus-smokeping-prober-0.7.1/example-graph.png000066400000000000000000002471761445353333100224600ustar00rootroot00000000000000PNG  IHDR!AsBITOtEXtSoftwaregnome-screenshot> IDATxyg&3d{]9TPPGGGmT Z+'QUQ<(r)7,,}'I~2;d]d2|'3REť= {aL0&^q2Ld2L& ?EfV|N(c!))L&S B'2[hQQ51XDR) 4z諯no%0*!acʂSN^~i?^Bȼ>- `% aHB_O=[!Z-7rԨ3n/**3w9!O>Ԋ+23jNLQT] @_K&q(P(!>oͧkԬ<묳 v{yEynnn"|xwgpdǎ077ϳ-Nc׮]LBȹG>4xn&aa*`Νb#˲>w;<ۿo63k4M6qBgi89j$!dM{w]}vԩSsee~L&8]@BEQwO~r [6o`ܸ[}k/So&eŇ<Ͽ+.!d997Ο?vxMvsl߶G C!dժU\s9+Vi7--kk./ƦnM|q1b) !\vȑGymcC#!j߾ŋ]|񏗽LOB>w0Cnm'?)>|ǎ#//o„ u*#]U @0&~7gE}˿.ݮ;w~5H?MQ,6~p O2Yp|2ao) -{ C>7t Z)6f;v,!d` -y~ɷnYdSL~.}jP(?'$.^VsR&BȊ+gyȑ#O?rƑe/|AAg)6.y~ɲenwEEּu]7gμXNE|yp 6t̘1q_'S/$S~k$T~A0!kY66eNG$r X5joԩSoOV<ۋ9tBhߘٜ/BN;K/u' ]LD?Op,\3jrj!@N2Ə9%KK&ee*V ti}j-ZXVZٻcǎNqIfǫ=n)מq x}ko.hJ{]`@>|NVE,ßu9FQݞ.Ne>!7 $.f=@˜ {aL0&^d/ Ƅ cB1!@˜ {aL0&^d/ Ƅ cB1!@ʊ1ayE%\0LSVVV?qVV '?wF7\Wu XژU{n߾aU'SmLXXXk'u1byG+**Nߞ{~s3fqEQ=hiiوF<أF:n5 bwayq\uuhGck֬).)v=k֬iiiJ)2&j,Bt:!dCP<OLBxŗ!['sgBNW= SdL8wޜK? ,zo۶].))RT*ՙgh4z{T44jmkkxSCX ô98!PUTTxB@'`2)ZFqb=v@$"c§zFVBFyw̚9Kl:$!=<&I5b|NN!$^ЅFq] MFU"H45LRīLBH0H$FQj}4RUYV'8Npb @@Պ|(Q!$H‘Hl6K //4RST:t T*k\.멬:.677BJ-l6&'ǜk5W:BE|)`Hu܃Iav:]~cbRB`0W,,X{uuZfb1)o˳JM$nnH@ ()):v{Ie*xDjillRŽW[[ćdZ%ln_{<744lVhGZZZ ZHy6gAC]abz (rJD:ww=BZP[#˃ҜHtdfi}{yNӃD9m!$YTQ^}}FR"Qn8=.4f"2l6[,)9˕j1:]Az=.WAxiiWABOrZZZ,|9if;pL-%>z!/ |Ap{fsa1 ]PR7A81,smmpv|DMM4M;,ǣB?9~wzT é6&lllx )}®-_Dp8JY}\+PuB9 Ƅ cB1!@˜ {aL0&^d/ Ƅ cB1!@bd.fq^wmv]lQQرc5Mu[n& Ì?*˭VQve*b\Դo޾ESP +.\@Q%3g/g/{fs` oz׋?y34hys z' ͛;rE/Jp7xG~1!\ ~=wH$BQC?8k/\H$m3Xr%!d1_YYYSS5?Zmg5L4)fԐCֶcc2 y;vF"BH2\ &l6~e?|8M'_;hgV=={fW~أ [)e̙U#G-p7yse0].ڵk|wkBIO12_|ӵ^{7F E?qgÇ9v&Re/ڽٿ<+駟y@^x?{㋾ҳvѣfsE>җu>_{tr7߼l}Qg  GI$<cY5MӄCx<ކzZCQԑ=-WAptP! st(^ftt:4xM)rby$QN'ϛ ANW2$ֳRT r:]ZJw:]zNz=}D1%BIr9_mw: ǝN!}!$8.a!!Aqmz>˭}A\{XL<"P!IEyZ-'+t BBVt(>DNexpXZ IDAT@ t8Ο^1 S}Hyx<.!.Gw@ӕH>[NMwGN˪ B"^o 0 #B $]AiZz}Oʞx᰼<ǝHԜӃe ED=<27y4uzd_i4^"^)ʋŔ9("/tiʜ 2E 0 uru72l tS)z}RNibNr4!//czT,Ry]LAz<)9z3tXD7"DJHRdVtj.=staA5rqEN Iʂi0Sө7-RrINb`#Òat ȿtA}!AH|&ҧ4c_8s#GpbE1{ gn))HwgnG41uX`0DŽP(csGXxݺuv1ydV !m޴fҙ/ywPM}H'.)-v9[|'V"aP :vyZ-Yfn߾r1\jo|g?ٜ/] ӧ0ի?% g߱ k^q]Lo;fܑ06} y{R ]dΜ=aÆ 6L&Vr p Ou|k> tL@˜ {aL0&^d/ Ƅ cB1!@˜ {aL>Ì?JޮRƎSV6([u^̄jǏWXXz6|/BZIccþ}2ju}}M䱾OSHߌ  4w!C^|8g/~’kڻh2ͺUVⲣIAA`YjPY٭[Ezu9LVDŽ3gͼ暫_55͝F-`L8jԨG~jn6N~tMSMٚ:̸cF,5P(D ̞Ç|?r &NfѢ'Z!dСyW_s{+; pjC=֛o%={q@HҞD׍3w("$WV6hС90 sウ!D#s8 $TWW+*qM7St?OzNF}pp-=ч~0BHmm]gRQQ0LuuǷ ! #4iV}m۶o۶]zjsrM-鋸Yg}믽t:9m֬,**(#'4M=KQ4Mhiwi:4h2t,K,/N+FY^E<Ch4-DRuhRV,F"QgŤYN_3EӴbSfӺ`6.F)1TRًcZ;sK[t772wgnU/4ņ'T~A !ܻy-[{˳sG[QQqEXnwo*frE>ЃLuKKp?YoK-e/^IΙ{1ϧx7_/_}s۳7b!`S`(?x`8ZPVVVT<ىlll,gܹKq Ykذ@y S!*~w '_kXvy`W;aBH"8ϋ풉'b><q=BBHuuW^z6n$@ӴjZIJ,]):`0Xh46е !''eY6Ѕ ikQh,`ld22 .P( dǣZnݺuX2eʂ zF"BZ'(p\8Ov啗x\C+..;g=tO,Z|-Ӯnv'?yݕ]PE99&7@h4&fDa|l"L&S"Hq5Nd2cL")Ge옐(f/_~ŕW\|=675B -_R5t\F6l5/o'?=[ // 2d…nŲt)Aصk%%ńH$p8l<# :>󕔔HxG;u8 H----,/K666Z">L$MMM999FQlynt:% ;NͦѴ.**Tz>_?.;u֖L&;---,Zse5z,f9`h//ZV';|4HiAN+kmm%ܬhrs;khh0999 Ñ򢭭mVkVˉ-Prj{bx`I<'󅅅Ƚ_}U"_jjj8.7Yyxbz%Y9N{*((`YHy>חZ^Ǿ*r8J-v]Rj9d2h2L&ӑ{jy #{Zaaô׻ -*mLFc{y!4%HJyAӔ (rZVV-Zu:)]~ZXSAhnnh4]AͽBzNATԫ 480r\Aϖ-߹\byNӃ^^Vc <H$z]Vno!:+SbEE2SNYV!ÒKuX= Z{G? ,(g.:,w0v谎iFK uXn;RtX= b1RrF2D9s6,vCj̭Khj37.JD<12[W$2 Dx\-cxjb$ɤE[ljDBqQT| ]Z9]sdåocAHJA'ki*i+<3JђRbOK//Ӟ,/>8/]7kOj<O۸x\p1Y3G<[AAqB~|wW<#.:~Y#rrLV̙sFy~k_|%y~_QcΧ(O!oq=/\tS^F5͛<_ ϛS~x'^vO|>/>ܴikdRoS{vzq@HӦM+++=tYg(_` uo8&O|ι ZvO8IpY#vޝL&>8ABZ$$nÏ39a:qn|PaaqpmEy׬>p:6kLeshɤ^J24= zU 9h-IoJT_Fb0YV-=i`3L6e`ЫT*Y`STj^S'jcE׫P(OSZkeYAOQKWY _q*U~r$NcYVb04ce-uʸ)kOQ')4M .:[juG E몣E\W]aEyjuƍ(Etި2i-ړMqf*c,Z-H$S[2!}OHyqOS+bp65 ҃ = |ZL9UONDzʽ1}OI[!=4eZN)z!}202 U2rG0XVEy:]ʦ$rk'h4(O҃X{Ե~Ue dX{5q\ZْC.!AH-ʹuAE%9!7J]:NK4ړlh4dRٟZNQa)4autXZ6-f>02PgnZ-37r|ܴZmZzy֓KQ48R2/BFs_W^WUUQYVs,ly+V?^Kr{[T*˲MO=Tު;܊.pŊw[!do6lhẑFB~#bo_|sΨh,w^ߡC^N=p!  t!pBiz3] ly;wR!k 64455t!p=ZuBN~?i-I&͸ݻvoٲE1euuZpacCcIIysy]<%rk-;lQZSQQi_t3!3X7p4/+x=7llllڶm{goAʺx6[lٌ857b[ <+ð EY@'l(ǘD6[`P\Sx<h`$ BFkW]}u]]taGH$lV ?}~B﫮<uں>Vg޽{67?fLee>H$!lРg>T,zÆ999W^uԟOO.jUUU(ɉbXL,B4iZl4QTX,*,(qH$"%'Iy2D„%HD"aҊ!x<(ʋF-/V^Xňɋ˓#'/F,a.^yh4"uߨ Ayb1bh4'$p8b1Mz)3w{Zߖw"WeBtd8^!-XWTH$HT#diʞ)=iiiyNirwސix>Ћ =;D"Q\UB"!֦X1CPTTqv{<Aa!ceZLN;`0ڪȩ>+=fƍӃ i$inQU^^&JwXAhjj`6;K/HN{MV8pC8s;>gnf&}rvTOr v1tPq\QQQs saÆBxa׷*=E4i"!djzĈҳcf!**LVWW>ѣGŻ8V'9s˳X,s7èV2x৞z# !cQϹw!ZϺ6mSxo8.b1so!ٳg IDAT]g3d`^7iҤs=BO˛&V#>ܯѿzG~{ !mmE?j'gX݃w}sH$~<4.񦦶63O?#H >WSTv{_ӧN.j4͛7/y~]-'Bnj;rssF&ܱ˯644̟qf+).YVk4~Txy`08߈lذaÆ &Iպ\.OD@1rdH$ҢYnJ&y@ }>I>!0&^d/ Ƅ cB1!@˜ {aL0&^d/  PTcǎ)+:p`gSj4qx=ޭ[vBN7n\Ļw[;VTWߺu|Ç:aUlҧ ׿cBżx\k޽Fi֝wZOY^Qp...9k__,U6[cK/H$үo$ҿc[oj6vB~~iӦ}C)].9s#EQ=Y3rmss_"O7mDϷ_{cʕc`~eeeMM̈́ /7gϞ=-W^IΊ~}'ٳ!!䛯O6T1&dfM;w/%߬0af tMhre?|M'_;EiYi29\.gIIizM buXRIFAj"z`0\LAh4au}KB'G9źbR h4Y(׻ܬV xAI376d@V,!P/(ZO1}sG[QQqEX{+z?wW|#G|| **jeY-oŊw_k⳿// ccv׿>{׿W_sFEc{fMuQyeee8n!ҲÑHtkBqq˪kkk8!4=tЖ;е jUUUI'N;2 )nSX,<<{!{GE'_kXvy`;*:3~oذQ(֯VB8?͛B&M4wڽe)ŢcTL"TT:}LX]]}ŕWx6666m۶T*UeeeۢјVոFZd{d'0!d9Z`0L&See%Ƅ MNla1&\nݺuX2eʂ zߚq^N&JJJG3=(o{!!>21clٲeĉ?vҬ !Z%1c3Y'-i]7uv՟#!D`0.c>d2ItÇkft8 \cXnEK&999RQTFcǝ.Kza2t;k{7$ mAڢ֊m U!X ģD+Z+_bk[/D ZNA!HwLfg7@χLf;yg>;X,fYi!Hl6Rd2y^uCtyHݦD^$IXjLy6!<өދg\&uEs6myTZnv"D9%Qeiˋ&EWd2gW׳,Nrl6L&òz.JrBVEݻptYY=鋢ǵE )T/GA͆PJ$IKZ'Q7lNy@w*DhԴbx<.D! e2 @p剢=)}l00L=S'h(JM Mx~yOZa0C`d2_@%˭ ^:e$j3L(q\DcePh,JjD8SB 9o!YW^r}>.)- ԁ$N']c1zm_mWAOaw VE=,A۩-y%%^\ ͦRp D"LF.P$=*Ap].WA!J"(Ibxvy$I_.Bh۶|1aċ~[?Эn-_ƛnT>znj9zh?zU+Ws+w3˟QjsI++WVݠ[ҭ[?@r9nD◿ŋ_>d(Dmڸl6]z$I<Ͽşx|jg̘βhܹs窕+{k@һsBWZf˲l*>۷wﴫVhExkС?'$ "T7ϛ$I;Zy]]]]]04M }LQY#Ldw֐L&$3 0p.sB`N `9! \0' 0p.E͌?h4uѣ;,?"bҤIUUU ;x/,\-I\zСç9Vu s… \+kjo[z~dm2j˞^m6x'f]Kmm;_jn3g]?k֬zջsB;tжdK/iW@͝;?ᦍAg=x׏@įwݡ g͚v;:7*+ك?ݲe5׌~ {٨w7q/y5WGʄ!x? Lf>AB(4ibUǎP&C@ `0u-_p]6\da'IR.!lF}Q{A~1'4˯bɒEQKg|y,WpҤ+.bw8%*IH,A$IބD"$I\n% fá$) vu,|$u&m2X %%R^yyD"q\IIB1Y^O~yq0X,v][X,J h^y qCQjyd2YZZFORlLP<y><! l//XV/Ivd2r1({hT+ ( p hsZmNqey=4Mwu'NlnWAPƀŕX,&IAKt:NwB60`i XAbi :`)ώfju8`fh{Zh4dV.$^tWn$WnFqǏhll'Wn X̕ X! B^zIiͽC@'Dž&!&NXq{ccc0qk 裏<#C^8iң>e5k5s֝w[fΚY;jyrK1ʃ cs9FdE\ѭaZXeV0Catr9F[$IAhrU\$m 1^8N² c'lsܳgIӧrna6׿K.njj5"v^_} xcƨm;'WΞ}re7=:1Ng3ֻv~̝{M? !tСeO?`'>0k֬ypJ:dɒ{;Q]]]]]07O٬_|P%b(,#;,h7L&).0' 0p."0 IDATsB`N `9! \0' 0p}m &MTUUŰKÇ߾};˲+/pbnjj!I}6't8O>l޵kb}WV~?(;{vNx5,X8MGghF}?X g͚v;:7".ҟ=-[EQovG$i0qh4bh4bi4;Z !j[ $I/H-F <5-dAyѨ+ ˲3M$پimJ1Z{ шP{y$IPԺ.O0 78޾'tEFW^F$CC/O)FB=RBㄶhVO#ѨhE^ EQžY:8yQ!IT~K lOk;p eO3EQʣ(*oC Ei]=} }3 A08-r BN=;T3nD0h7Z^)׽SnHޣtKWL=M?"tup P BgaXQA(8i {Zׇ2O=MN; N# ɞrՕ@ޕ[ᾂ+7T|A[/J*#vذl%CVcb8qºu]˦<n}S$QZ&MG^v"B讻ꪫ(Nz}K/fMkv VdHU'啔v`&z88k%IJŮa^!t:/8U]=@QRl['xl愋U^^`BBÆ s+BMu[ <,gꕲ,7|s:)|6SGy%e^!ҧng 7[ @gs‰'*BPccc0qbsBPCC \.'r\nΜ[H6q%,ˮYϿPSS33gt꿊%IѣMh4N:!`EEdFQX_PY9X}h;b ~H$2D]s EiAjˉE-MMM645x\Ng볾57())-l.l/!J%#R}^<aê \ jĉTm9zxn+- efsLpdALJx<:D* V[[//o{MMǬVS^ʲ|htZ'N4xmr9&ͦRp d2١C; Bss3Ivru]NBT*VU VEaH.!KRATQYݱcgwia00UUڜ+*;~јW.(;֭ T~04M'A0-X].۝Scb0D رcKANN++_q;h:_^@j: s'N xKA:tA$)oD8ȈPQQn2uX,wUv8:2l(i":0'Җ-[ )0~CԯXAhiir95$2݀eUЖӞ^ /lhh8WngMqtZNˀ B/9$I]bte2'NA8_:e';w $U7iEb6O8+"c۶~sرcB$IΜyܶCj3f1++BF3fȑ#B , 8ֽ2vnFI;z̘G\'\rU.aFD4J/{z{BGS_9LӴG8ٜP!r'NhVL&]'s}}B}0p.sB`N `9! \0' 0p.Eu!zǎz۷ogYV."u<ϟz/wc/XRuN?(;{vN8i҅?{g@o;d2GY+)5>×]vkk(Ewo׏̫j?>S9\oz}m}_{'Cqc=Ϻ]yL&7lU(j?aϮwۼCڵk(=vnMP(?644 L&u]7nXN-[674fW_Ȳ2Mgf8_}7|k >æ&G$z%^?_fJeMWԕ-+O%Sڕ3:d';?K/KǍɎO71.{?o oL?^1ln9T)2Lm0M8N ^ ÚL&oKd0LR]`Fh4 md(JӀLFlo0l6DT ٬)F&l$i6іa`0'dɬ-0DbpD+8N+ bʬ-O 6tfSAyF]yfQS"je "rh&I`Еg2R9j1a&nJ#I*vg_ɺ}araO3M=ͤiʡ=  'fswКS<٬- ,t:A$Aw(I4;\Xd? h0ԝAP .f25AP< ǕAP^ANNuW-ðT*%rajyXg. `iAw({d4v X ԭ ԀeZl6[(,V^O,YNWa[A(ipsړ +7qr}rasz_i)zr;q B BmD!dzi:?~w<[o,}b֭h)gD _Mϳ+~s߽۷_SN%˗?i&е^+]~ЕW^h?q}2|/^ZqcY;x`ȑ5L =6zhCCC.u-_4 G/8>zW[bX__YYپ}` 5jd:niu!JB8Aٳճfzg2 w$II-((E?x?:ڗv#LOzZ<Qe=m ~+WZZ²6z)yv r ð Ţ> 8p8dY9!P,xpp8'9!IK'\NwoWfsEW%IBLK!r\-<}`0au^",\.qD dV2)5}DnSI`eYá٬ C]s&$no7J8aZՖD"a0PfE-&LLFuNfzl<DZc cj9fAԖNmXtԋ]IRdRov+Y,1<ͪ>2,䕗8SvB$I3  dٜfS?P2$v!d)jh8f/X,g6 8.ӕ0 _^SSR)ǬSzhTd$1imA(NA`Y]O+R,P,Ah+Ϥ}zLQWb0]TH3rB9żN W^<7*N! -OǭVkSSS,i z- ˲]{Va =z^: qÇ pVa:]X ހ$(JmyA] +7-pvf4L\4崟,+˒a7s‹/\\^޾;n6;rnW[y>Wl&D:#Rx<Ň}U ARGnպx?qīs9&_oHSp d2١C; Bss3IvۥA!J"hU`=aê Ñ\. B `;A0,:t;A0pa*mN?~h+OQV P 4LvEKÐbAht\nw^N}>:T+A(++X: B"鮃BeegA8vuAGSKJ|_~y X6 !Iê4X_____o1'Lس٥oǎ;v y>7fV2}B5ǏWDNh)^zKq%vmeWa^1c=zo }5|DSO-U_◿@| B,Z|oðL&^ғşx|jg̘βhܹs窕+O]@/愢(Nj徽{/uRÿ~jlh4Z 6o޼yfig/liS}?psd2L40LKp}0p.sB`N `9! \0' 0p.E'NX=Ç:tH#F? vV'Od.yÇPp9l~GkjF߿j.X07V*?a f ={=IGw/[\.jӦM0'97T`'B?k{w>|7xvBG^麺["Vsn;r8gٜʄ!kZ]}pW\6lxWсWlݲj8Ig\ xvżՌ G֮]KG鹵skjB/!d2qlvlٲݍJ*#vי .x{߾}1I~_?[;/}Íee|G,-- #X ͩK]s~'νax8`t,v7SU;!tϒz%~7/*(F !$Iseq!4rdM<8q`0myAey$F8dAbFQe HR^ʲq,A$:W!I y<(${cpֱlcذasB0@:E0 #}q IDAT>6W*<Ҙ1FSRq(wھknu]Xpl'+$!rYRNO.}[n>}Fmmm0ܰ~úu BxD"B,tq7#GKJ|T*NS|"`T]3MӒ$|>%$m[sl6;!n~,!|8ufs Mǽ^z;$v*JqKKKԖh4*-P(]y+)vBW^29$;&jhNQmyGy=^㸏>numN ʀ(U,B(h4/,ˡ~^ߝ\ajiiillѕ-V0 ѕ۩X(gT*%vԌXm>\9X///.Y^Q/ߏJ e5kjjj&_Ǖ/B(m-O s*FiŢ'H \Q#'H=a)-A[^4yza!b4J˲h&B(m4Դ$hZ-zd"WOXʦYSOXJ B_dmF t *cm1(M(JJK&FiɨD"I1ժd*T8N9#`q\$B#$3MڧңQZ%My|4Jc&*0h(JZK(m2ԉD"6Ms)Wʋ B4'\r_0FiI1u}2 o ,3gNie}!aϦIRևC: 6L(˜Ԗ0L~v͵}o۪19G F( Z2sg3*-#-堶<Ðh69E}N1 rkyJHX: B2.y дrAhgS&Q-(5Nbs䁌ضj*fΜ#r8qh邜Jj̙SNQR̙S>qO[_MZ+DG%IR Y$I֌QJL0o &}6'3w(np̘}6oN&YTRs8 .lB4MOr{lS|6Rɮ6Xn[|ٍ7ݨwznj9z{ٳ^x|'os~_{=/2B(_Pccc=>w?i-Bew_qXǗλvƌ,Ɲ;wZlNxfvow{<G"y~gGݎu;vbFE Bh?/iW]ԗuuuuuul64 puPJRiV2L&{* 0p.sB`N `9! \0' 0p. @Ǐ3Muuu#F? v]xDE}9'Z o_xusf0k??0ٳgoѓKT~4uԻ3 QodxhS_8ٜr:m#jj?>|7xvBG^麺[:sM=;j]j孷_>'j}6'4L_;n+.b6LK/U~qܸ,e2BH9>[ny`p ֽ&EaXYYi's:&Iw XFpln$I(0 s8$澮 6pœ(H09a_^^m)(7_|Ϣ/:tcYv͚O9k^juᒢ(y<drB1 DJJ|F !4rdͱc}d2ez=VRw6[kyDžB!׫^rh4ZZZb0NxEE$z"O2 E4DZBBXiio4=N8a۝N򒒍W5CDb\ mZbJ\OD?_3fdl#^_:Q6'~hW&>DmώV}cno&iP[^>O(/݄a r[>sMG9 ]JM^ SecjBoMpO)lm~r>7/ju-1:d}kyNLǷ>&]Moq^6\5Vya؍izj,"ސ~vwGNuӳldsrL-KmY9tGL4]_auV'_<#UZʱo&>ȭ啈CjxsJqTՏ V7h/p V[Xcxם/-]G=7wqTپUd$6ƍf8`0% l troroh05(l-a nƲb-ڮٝ~Ḍؖγg3眝YW:-//43 +Ḹׯ-;.Zrɽ@ٗ5_TwTPwʫ8H__M3:}W]ʦfz߬~T-~~ZSg?^yN`0 a uG>?LI>w5hI |Uw֭ b ~Rpe'Ou+ m7yݞ>^{_V6eYZVUdgOr;w:³g_rxJ+ϼڟߪ;ړ+.{6}z$iN9+gGz{K|F?;oQTU>_oH58"k9+~x߫;΋g͜z|-Iy?/39{f%2UHtfz# XBx<ios1 XʽvEϥMOH|̘$1~_QQ!EҋD"PD]Nh4VZMϗL&3.ۍ6>eRsfZS rdR"<z,kLO_H8 k XjrȞeioo`t8 A.nj1sCS9s;DGGiKO7`w斫Nyt CNsB0_u+rzG*u+(&2VFD\n۶_lP!DĜٳ>3W_}'p}_$IVUWoEh0̙;w_EIRKYIApDQE$I]&EQ} (eFDׄŌ˧ɤ IidKJ&YFdRd$I]U>.=9|$[d2ݵCez$%o6Lfx)X>O+Lfe %j@ R R2ad¢S6.p0i#&HaL =Ɍ/JiH'CaX)m4"KB 0"%)h5"sF2e!dAfj#FB* ,<Bi' Gh^Cem≠}#AӔʲp8|[֕74oB%uf*71,U^uU,"N;::kܮd+B}Usݿ?s!˅3wEQK.Ytݮgcm@{[qK<0͛y|&|_|š~޾yyƷn|ѼSSSSSiSs-[fj0~?1'Tö/yö;kB`M `5!L^& ք0y&/XkB`M `";XayΝ;<ifϞk.}x&,2;|(Op8~AXM5lF5^zҋ/^񦕇=5^kBɄ @$%I~M8{Μ /`CW\権o5k>jjz<5O?ֆbYnfoذaf" fB(Y֞X,wA9$a?n?۸a˖]z> 6\s5w˖#ni3{ywTԕ+o(weA&'4?efxE'ҼAFXeY1L&0(Zql6d$n6Dhلa4m2uiG HFL&M0LA((2M;MSuc˲o e2Ei#faȘ;bXDe~> {Hp#aI5#_]HkݫW[^q.miT!dawn=W_@-Pp+4˜ߝz,hE?ەLȇ~^m7[6}{%[::O'FHԝr56$ٹ`HN/NF0آ-n={z 펾-i#7־ B,e<r}+HDdI`=IO eY?RTFU F]zdB9{T3x(U9vpUbF3 dԞr ?ПF&``aq}!ӲahI4e0 !;=MkRitDi+BKO]e /C+,*V7$1Xaø袋XZ[[+2ao75:x),X;Vˇ>D-^ƛV~hڵ#.\xm\p8 ϭoZk_5k&H޵{ Dޑ_ &\UUq_ &2w"`Bq|ڴc{zzȯ@AA~QQю;uPIkz{;B8AM넑Hx玝MM7Ȳ|΢W\ _z[޵K@ڽgvbE. --==[np(fw Lf,:yH$G~5$IiaNgBfw.`B٬vք@QPo6ujX0 #>G554'd2͛1U,AK$n!{}|GK,}'yv_H$8.(r\L;K&Sv  KIx3&8.Hߜ#2ŒbC$ BANO{kųHFIO6%=m2Jzڻr8Kdދe#T&-($Š0’x\GHDGDQȸ(V)@eܭa:Ih$MI4n s,)k.""4dg#)tƮm˼c<i<32OCf)Is;Ɍ6=6+=F"rF["el=DY2[*&) ,aAkLLФGXFd8V,Vd4G! _U0TYYEϮS]!B թa**X94C]ƲTYY&%mzc;Q(uեهrLBjuGeŠ,[PPFm6{^^1||{ غN9Suo|M=Jwhyϝzx'{9 ڿ+YO)%m2$I2a[\M_|@]/4٫Hn,Q<)qz t^8%S~i)k6HNmis^)73*/s{o?_Bwʓ?ܫ*{zb||_]J,Xw7tG~brWqW}ޛLg~pdATWbOV#}uu0ujA8p;??bIs}}"1uN[h_.:?;:z I[vIT>eJ4sֻ-uPdYt8lv{*d2ݓX,L)2R xee%#:`0Hdꫭ~/3= d===$L&s~~ƈpv`zA7`!"HT}FF$I*)n$#B"lƈ5`X+t$aр1ІԹ={Fcn |#B] _مx<חNKKi:N++G|>$^v!xF!tuu|eS)ݦI/y Dޥ-rԩ uJxQQ:.ɤ`&̍}- A[مxDYYF82en;rv. X>OWD\^xvgU#.!$ɪ( 9s}D3ׄ \vǽZ"/;nժ(?>}Bl"JJͽ{[uWZGb1Z?x L`qM;ƺK5@ #(>!W^qW\qBW_sܷK.ya͛7?c7ׄW^~_#IjsGMMMbӦf/nٲElnnnnnZz#&PG~AȯoQ8<`M `5!L^& ք0y&/XkB`M `5!L^x'06lb,H<+*+B8 G &a 矿xϞo9ϯno憎yh?fWyEŲ~pׯ3ׄ'r}cPmmCw_uе^cۯ_q}0" <qM/ Dz<JMg'r(~G7=g΅^+_QsT͚5~e55S=ikkC,[WW7kL7lذux"Lf!Ba!\.֭[M`Ϯe;9;'"IǛMp8,IRLvҥK**+6yO?N5k-_|qӼy8ƷB7{7>8-[v]$imm*=Y@4B1q!tI,Ia0 S^8˲4MHE4I,KQ!I``I2(e$3#,A&a4M, *)0 3R2.$Kemz$I,aEQ.=e hzYufz.=I?IH~~`T$vb[Gn'(DkXڒq!DӴn:w%Dz$OHH.^#R@I%HRz`{җ@Ix$emz~9R|+@[Sw\'xv.%c۹OHZQg4'%"ʒK O899-:Mzm<mGqrO䰺oKzR4kKzR8l X#HHNĶǂ!)9R%{(K>> mI;]0r(?7(4 }!ujY)b:ڶS +4%̪uuJu }vwf[e; ,A#4;b1~;:q>2i9ڊe uO,/'l:SnY:%tuJQh aҭaܜN( F3`A374B錃˲j=\wN`B88B XJb!d^;o3g/e8я~Tp5k|>II?OWW,XpgVu"/^|M+W?zB .[.]zY8~75/Κ5O$vڝ38LsüL&#;0!S9މ Z _J!t:٣N$7uju4sab9Y$d29H$ j";"IWSN!B8?6q[o֜{.?Bh=M[[[( 4nݺm|&O í0e);0!8v$կ $aX~~>Iyyy fj5!PZv{뉻&4ͿoJJJC] &IЫ8P x|XVeѨ `&I!.6xo$#S=p,Ytnkc/b{{{4: eO&pjt 敿hj_ӧW̜Y '֖:f5rƙ͉ p}\y%wUIҥ~={j\O?u@3MX`EԩSN9RwΘQyq/N邏kfт.Z>M%KNkmٺ5uh_~o^%b=w'|S"3Θ{_Jk_}zݬYG]+;:e1NooV# ~|d2񁁨nWo nuFy>PwGdY!--NP2ʙ `zB02ouǣьbqNS@2t8eYL/HŒ>~aҌH H$")9Q^(Flڞ$Ɍ|>LFM^v!$|$2ƴqLH&H[0 X,ΪD &YpH~5h63 !;\`(5=}!p\,p(j`3YҭG$)NVkt=m:M&1\f 2jB@=M`!X՛VpX3 !Nu@!|#{0/Z4㏿9֏Ϛ)˲vܞ`K^L$'\{w ?=eo[K8m9'մϗJ<ӧ9tܣ^ysAHu9s:6>cmVnQ#?ɬ lXS\t=_n޹sg*=袓7mVW{N?$]?/7{vS+D** N;ط ?(3g?jc_~SuGXL$XyL5b{-7ոB BjDWƶ/{f/r#|q4$8NQeFԩ ޗO;xQzOΚz&m/<\{VG}ͩ[~C4}e˖J^Pݳ'u'mqIu}Cݗ_J=%Mw5O>V…ϟW#gX-;k׮Jv/Sgj27/}uL]Qokp}%RV^[PiӮl7\MygY?͙5k/F.4YBo+?{˖}}'Xqc꺱 }}n3O|-I:FqNMYxIL|H$}?H(3B D_ !Yd1XP_n $N%g$mtۊ ur\WL-`W^o ڻңA+I~rLF4؟,}}næNd_i6㸸e``Ӌx> }b3wtۇ0i|hLEI8!IR}! )&!%Is;v=|D]P 'P]""H$Iu_@0 /@9S$!mzniu>bU[Ož>w^C*d2XsbRX,v{0 ST8$I? O;ѧgeYMO-T UG* ՅD/z$e@yWWQDsJQ)0+BUBxr r[Vl8{啕 u6nLXW^Pkc&c:93fTkK/=MDu/niVhL$ w]nڔLt}C~HMyvxu.o}}0sfU}C?A]vK^| 5%}FwBܰrܰ>0nCoҳٹ3PflkK--N-oï]T[͞]UP]ՠb IDATMxjsƙǝI? /8,]uw`{:WޗoH--ho{olޜnoI mёZZ7M̙'jj.̍5к>HwufgnyY3mڴ4s3¶m5QG7ԵimM+oӝB8_DMӎ>L:ŋsL+ؗ_C.Qag\ߔ^c7ܜ*oͯݱ#US\PkwW[[.okjחZc+ Mg͸y5a$a x7r=Pwok,=|"SVNk$j4̝]? &p…78jj=kK&zKuuunӦ]\w֎qLżjՃ?6}zGG$0M5 'a]w5h,_~=Bn4j(J>쩧ܷK.ya͛7?3`" =Tgu8P˼TԔ{ӦE眫nnٲElnnnnnZ h" Gyu0 ,`M `5!L^& ք0y&/XkB`M `5!L^x'0S~8w? H|ѿvtt{t?q7o`hٲk{O>\s5E-_!t͘1g|GLꪫ'}ݡ$Ie B,˲a8aa(eYy=MF# Fw`YHS#F#@:b6$I7Z,dRjn7b|"!(lhO&S-C$‰4T2 C1 Ԉ@$Ո"tbaAt2V<*=ǬV@\D%B̆1IQz7BCHR#ΠFq@檪ʽ{<׈aD޵<\i Liw-KB2c@E w9$ H KjvrDD0 nPZO2:?9C!$#M.^]ZI0@)6=ǧM;?z#aHoiCRH$IRRN5`uvO$S=K ;ls"M&fN&rzG]\(DKn7q\BMsB8Ak v(Oz<YǪUp݇ŋx^}… oK^M~zfݻvg&ኊr+X0nOj0 R ?B8WWWyp82޹ ז6 LnL,'X,F~ '轣#:E(}om$ewR'#v٣ݴXpuѹ`KsKOO֭ۆz7 Ê f,{0j-(W4a'I2LR0 `fZ%I2 fj-**5!PX6$vPu NPee݌㏻/R^ \,>)#S=p,Ytnkc/Ν߄x<^XXy>9hA߿ Rm;00000PTTC;($Iڋ$I5v jU?X,&IBgX65x ˣiZmp8TPP{7euGP(H? ˲әi/)nhXYVѨDɤϗ^<NgEpaaz#P$bEEEꎂ`2LϏKzi\.ɤ'L/;ðq`tRDh$),,TΑH8t (ifi3f93=јjD"0IԦiھ({qHaYjM'˲֦'BfS' !q\(^v!áx\WAI3 GÑn=òZJz gR K:-//E񫯾ήBЦDhfzDbB(j:=`YvBSm!S>ðT:wu9NWA3Ḿ1 ](x9 A[مSeY޺ur7BC AIoV0$e¡00B+Ft.Ԁ;GMflr:q,y7 A?`q\< k4NO7sCF!tOPf8 !#%]q=.{+.?~u.+d2q&ŕ"9.nj$ugDx3xpu8..ڧdKTE⒤$9. _tхyxG#lMHY?9ɿ=/*ߑ̹ V?z+jY/[f]3mmm!ef͞i6[~ }~*'Gؚe۷] ߟL&_簟|Ivmܰeˮmhhz=|Ɇ 暻~}eB7޴rƌ=;fʕ7P;I4M+0k~J3 a!aAAQ4AMS$IeFh8^ D:a0A%=52+=atɐiZe]2c$ӻV&$C.m= A(I fO(T[0 gDʈd/BaI2#E%I!סԧ$dvO>6MS8Nh# 3-G h:ZQY dhDi2#="$ʌ( 8}zم0bO~EQB(ha]z\(=mMYf&8]c8Xc840TV8hPOѝrsFZҝӲ,]2C40.7W! nE٭/F+|(3{>Tt@Q 7)wtVi${%C _hwOjFԡePk37gnVXTnHb!dÎG?Q}Ýk֬%%^?]]]W.X;Vˇ>D-^ƛV~hڵ#.\xm\p8 ϭoZk_5k&H޵{ D)llkkx&2w"`Bq|ڴc{zzx&;v) >v``o[H!vPY:?89o[s~]~={5.:wQ0linݺuP{q| -7ɉeY3/$$IJő_ & ÜEEz<'˴!OQ#`Y@l<)Ngm{{]EEFQDQ[ZZLө[C(WH*z'H!f3I|B):`0}0=NŢDq=eJ:^oiia0 sھp݂ ^LI^ggdW6eYt8lv{dwwOAlNqq5eL/F,h4VY9\!ȅp! AWH$+//U"`Jݑq\>I,re(<ϷlM!h4^o<//֩ !8ށ DQU!.mQ XtVv!ȅ`4$"݀eR aNRsF}mmmavxfn>GevP,]!"GؚpX4z3&uSԩU(z<!IUUEQlmmmmm5 s=x)u;L&曗,;0w܋.^ڜzt4,U^uUNs3i_`l(Q?쳧zz/_t l޼> u %Iz'yY 8&usӦE眫nnٲElnnnnnZ#q5yu0{AkB`M `5!L^& ք0y&/XkB`M `";(--9klr-<ϏwFpD:^\ǟx8nW>ҲN HGؚxٲe/+uݿ_l,] HGؽ$IKowyԩٯ=g΅^+_QsT͚5~e55S=ikkC,[WW7kL7lذuS8!Lf!BagPhdR$Iʫ~駟^9}ڱK.O?N;}֬_|M?3jD|̛?OALM#I`'MMә f'AaKdpe Plj̶ VmzL 6+=FhC\9="in~$iZKC\5 0յk=Pj[$̃;umarvO3جjzr(uGX4}p `M!TM`0hV꡴٬AÑP$i0 _rvz,f\}덲aMb:tcYӦy!(7b!NuWMðH$"ajzgBҟӔPÌPctШ L&lxܹˀ;0,PtTpt,01gnN3H|b\fnN37M2Xfnf:8B XJb!d^ v(Oz<YǪUp݇ŋx^}… oK^M~zfݻv!ښh4>0UUmmm\P^^Ft[NL8OvlOOo \PP_TTcNLfG];007މH$ wTu+EQZH$yy߂:I$mذqOW]*<+u]OәLT*͟?"b25UphΝc\.,oJ2opBsECCC$wu͵G5kVr̖{BabQ%IdGGr<43g|>/y0g2BXX`l ɲ!F\\\\\\\\\\\.`*),CឞJ2?z>dɒ̟I؜/^֬$/Iؽ~W7\.ڹ}˖9_O'?UW_} CGyDW#?ݻ !ǝwy@ ( / 1|k !$ O_=ީkW/aݍv)_ye顗^|If/Ra\u7,]GdN>ᢋ.z衇ߎFUU4McY뺢 ihQw.?E^4xAQ]Y5[dY&X[$cHRr݌VɲrQ͸\MQ>-oJAh^Ӻ\~{TjzrNq˛rPN MӋwϞ٬003 B[[W_ui a1?`™I'-f?`5,?s;FNb1G]1v欳Zdɿ|Χj5By? ůO=[!PyP(,[lm[n%ڵk=,F7A=,| q.v2-vES؊|>D"jUi)ЊiCi2g h 8VZM^M' Ry3uW:Vs,z=O4}xZz::ۊe= W\JjZ ,+X[#s9k^^^74~fB_z0 Boo97ϽΝ[Zp%IN4oeX,V銢Nw-"6? xVvsɂ ͖t:v#ْL^o(ě( әp8zI B.+r>_loo3/mR4k-Ĺ\N6%j$-CCC <_WѢ(p& qHyj5Q#r\(ԕW(Yc[/Bb<O8 utBR l6j[[!j;vf-/bY6M\^(ěÀQ^46o'E1[{ZT.ʳU d5m<0MSTjPhZL@ 0+UUM9PTB}ybq*ApAAMP*J=-/Iue2B<0$i;wrZ3bT:gUKlB(=B!qj{&QUͼ[ULF BWdۭiR.3XmA~łyD0,hYBj^zWz&#hn)Odj#Tb� #JLF8<l.XNhy9UU!Dr.ms/d2k5XEQ2j�!T*ڂ7B$lv[0>Q̜:5f>_zZ.EZ^.uRӬ30!Z&kNs9{r|ZBr,qՋeYs/E[BP'R^=f)EZm<#n7B!/MI 50NN lo3/9iZL&+#Afy M!4o0`z] 6.2`U2w 4id2`j rJH{NegN -yG ؃ 9`|>YɆsaV 7Afs4 /e2i/DM6Vaj6  晛$lSfgniLr\*sJ* \|+VK7n ٫iZooooo=yC_2@;s=y800}{(SNs=۷g^8U7}SO۞=d2/_jZ%XqWd2H ~SpnP(B::Nazy]]W\q7_ޚwplh9 |MBH[[xa뮿tw&<^r%??w,KkR4o 7\w՚M8F vͭjo5r}/̜9s !˖/nzIBe˖3}+Vx* !D6mꚷh"#mݺtR[{ZgV^ݻz`߾>GNN/EQ7FX;oB%I?w~p(v{{w[%}a,opႅ󍟯ڥK>O2̉']{ 0oǫhG&4M9(B5zMlZfkql-TuշЎb5\$sl1B\.ī(bb-4^17.h(2gw4id{5V_L)͆[\&=moyOk=Yvniڶ+38K&Өנ\{x1Q'_.WÝk =bR4{߹c-.عZ4gy3mo.kbHTtNr<= 4*Oi}IOu^L˞a;ssΝgn r:37gBO#je>5 Šm.s=g"++v~~5k9.y{߻ƛP替v٥'I_=x.;wKFˍ7[pk@O۶uۏ~Բed/oe bѢr2888 ǟ@ `ݻwbuk5޽g @f] xGGǟdfNZT*]!v1-qlX%.\.zTUuݺZ_i*!vnBkgUE1'܏^ye``p]&X X-*d>7b1Ih Pa.ZMӁ@ ǫH$0'n_ DžJ\.We-1}Oj5kB^xW{DQdYbKTDB<|(DQB!8M?0S,|[[Gs0D"aۍ@Q@ ysB0|eqb9R='}ߝUH8io\\̙MVmmqchQvi~[( œ9sC|6cwka$Ia%~o81>88 Ѣ(J*F>hVL[[ˎ̜r65)/ Ů!+IYKӵZ}l>L&E- p8lnD"dYbُ(Lؗ͜J\.7k,N|>W,Νc(dY744D>v%H, CP}y@ 0Z44^g(vc|>?{l92tvv{G_|E]Y/ &8F#㕧j2D"~h$)N1Wb!/ԗ7W MS:::͖T*rRźj>#- wmRmZx,yO4UQAU'灥fiiG&AдAu{ܱWL0b?ʩ((ǣZmi Q:s3z!.N,;BZb]]>}?O=ԗ^|rW\{Q7sgqƓOl2͛?~rt^>H$0̪Uy>ć sE_d|/Yo߾UK\'rg.rS~'0{>EQr6}/7? ֭˲텍o-m/|n|he^5\R$ewرn-1'$<쯟O IDAT{<ϻnAOo}V2#?~x$ c]S !Zi!y+~2'4w'!\.OɲJvu_P\lj>!L1', ׯ.ֽG(=̈33333333{ s<ド~l㧞z*oQ|z?pi|gl67UZ: ?/tYg-+V~[tҢ^y㩎 _E'~?{j 8uEݟf 0]/My?K~p!WjY^smRin57pLi ì[_>2-{_4Be˖3}+Vx* !D6mꚷh"ے#mݺtR[{ZgV^ݻz`߾>G}Nx%h?Ko~sVg8rݽ-OBN>0S7[NXp~~ҥK{'L֮aɲZor?M,r& K` -j5ksf~eYkK g@vS. 5vlY'x^gyn=b(r,-0L ২{zkZg}mTؒm:^/Ap4k3YwnÞfVۖ8o}ӜA0:d`];zlAhS| cΞ6 X3z=^/g/QsÈ+!v8aAJ2!׺'sqIRV9`z zZsrA`# mp׺X8 B}yL4A€e9!9`o89GgOo=zj5x0Z}y>[ybȀei-hj X^3F:Lי̍37&3`3gكpPBQX͒%o}{}w~BY{ivŗ|{oM7ćB7.߹_2Znq݂]BO~ض~G-{$y/Sxu˕)_8 ޽[_ 3@W\dz{.ZMӋw``0Nw-y8X'T* &8n9BbZ:!q7|K?k{vz5MSvcNuqb7{Gs200s^Iy],- J֏ `&8.ʲ$JW nFӴ/X,J ZB(ć cNx ]X.o( |>OȲuח^/!R*!(OQq(\y}dժLR2Z*1+R,7r +JӢX^M$={6~n۷h4j<4mh$ yp0f~aRRCgwB5;l>[p!IZ#5l9pqmmmf޽{Cp41z@,5$U>=ryh(=g,s|>/y̛3X,,X0dY;w.!$W uvmxH$'̓(ܹa>_ L&s6c)M~{ dKj x| (? dsgO4`قZ__݈ @a֬N7l6k B+V\44mr:`5M XsZVI Xw_rX t !KyGurۀ70ө5<ϻ޽{܎Ιap0A䰜dlA8BN(Un|2gl!}DQܵkEQ{gW8eϞ݅BP(|ɯizx9 7fn{{4|y-f焕Jg|g4W\&=Opj{======<{^Al8✰r\.>%r*Bn15x(sZoQsb~BZ1sEg=Uqœ`œ`œ`œ`œ`œ`œ`œ`rOwcO|;kGV3㧞z*oQ|z?pi|gl67ZeNxk>ۢ(^r{ƛ$I"XqWd2H ~SpnP(B::Nazy]]W\q7_ޚwplh9ᩧO|;| !-c63 s׽݄_K.r箖ei5V*Zi~?Lj>T>ēƄ;4Z`>!dٲEMO>iu۶s=1<χ^{kg|=w/^8_cUb;^.q^Ci,K4!$zMUe)2^O4qlqFZ,ts0v^kaX]¹\.!EQq,Me9<e㲵8Z8,(a^[y+fʄU y~…9!Xmm~dY.ʫVrY^,3WT*ED~"7VlosB!s_r%'͕JYŒHl1$[?f 2^/sW_^м^/IRT'bRW^\$z>W,4M ÄWvܥSq}MrN_up$rdKqpE1L $ g6WeJ%(j"ǣHN : B>[gE7K|>A030\.s]֜4-r29$kӪ[sjS-9Z\tɀe "ˍ@ev$gقLxAK$RXU.Wl9g2 `ꃐ:`|>YI#Xd,c 6B8:Veji^o iĂun ٫iZooooo=y#&ZLK|嬳^_.Y}"!==jMn=眳8'd͛7c8nҥy㋈Ng}v$afժ<C {/2>b,ٷozө%/[XjBȽ{ w)*=/d旛B}eYEQ^ƍ6X!y?SUu۶w}|k>Kw\u .X)I˲;vXG'xX,~뛷@ dlg?}< ꄿ]~) W4`c-1'r\.>%˲}ܦn15x(sZ0-fĜX,_~r;z(DQ|_Ow-gF\'0'0'0'0'0'0'0'0']@ p /$Ix<~ꩧ|p```EQ̧8ٙzofsGjcU i^yWfϾnXqWd2H ~SpnP(B::Nazy]]W\q7_ޚp,i{Go[gOmlkg{?]k[Z9wɥ^eYZsU~붫7߼zǪV7_gk_}htӓO –-[4_si~zk¡}u7͢^Oo{w[c;^|%-X8ڵ_ |i K.}џd2O袋zzVs\ZVESEYX, - Ѵ,!4MOТiEQk3ɖq(nVqV^ X`;SJw  IDATvecӺ2 Ψ 򎡞F=p d[ø˞v$kkwttܹUqӎN:iQPQA8&ʛƞvx9BbZ G"|ڒ!H!/EQh$zPXlٶ۶nJٵkמ{$YnJcyaM8^|@ .I{4Pa.ZMӁ@ {/IC6uzrxxv%Jy^獇 Ao2< C7rz!Il6F=RBͼ*m9v"˲fBVbc`nw8hQ%+ZrX,0fyBf^F/J\Q.S<N=&+/L~FNZ`vu49uZ^X,dy0P a79AN 2{N=nN (rUU M{˲fy hN75 XsJtV۹s ay B(zG3Fyfu#1`كP*KAR,k&-  Ȁe;5XG2MS=-Ͳ }j>;bd0V̍D"gn X͙(X!˪j]V!HZʄNBH"H &!H\D2tf_!Ha W]_m/p՚LE1bո ˒(Vuf"UպIպ?XJժ(VUuEkXUձ VŪR)XS{y,Z$jjUVEkyZM 6SUQj!kym%I ,۶hXgg;4]Dɲj BeEQm-έW/(ZVcW:vGߝ| pi9[d%"tҢ%KR==jMn=眳8'd͛7c8nҥy;}ّHaU>|1  .ȸ/Yo߾UK|-_J.2BȽ{ w)*='%~P(nݗXUm/lxkx3UUm{{w׿C.~UW$,cǎ1@Kh9_s>2޳b[߼L&c/?H$_(T'2=wNQT>'\jqyOOOOO^WJ@9ZbN8r\.7|JT*5Xo+bZ(ƻx'i1#ba=݅cC!szh93:!49!̅9!̅9!̅9!̅9!̅9!̅9!<:gL$m6 "WuWTTP,(Z r*x~A9D(rRZڦwӦm@Nڴ@Ky=#dgN_7; /+%ϝ>wa1dKu#b)}YrL&dϞ=B0<@k|fMN p3 ԯ_m[544BN)99Y2VYݻKF=wv$w>gLڽ{M~TYY0uؾXdgx(Z4T&q湬FnqύajqϭPa/NVkn?nwWsٔqܑ#G/[QUU5py^~rS%%%o?O?T<7}(<SB>xq ^zZӧN_CI 8pkQTqqsssM&sG>!66F&@'4ڵ}N~Dߗ\WWW\@e4!4yBu3gNϟj]Ws_;/p$ltnIɴ(''o7CP桢cDzNbLJ U-2x'\lZV[G>!44e9( &vt,5M\\g AӅT*)J 5CǼCyyys1c)Zl6Bd+\)rJ[cO>9GF~/noYY' !:}QXX( ї|]|EbYvݺu듒 i5xt^}yyy9911a0TWW׈UTT444tÒ.),,86<<]RBBBk.]kZCJBIC422G*66}6`sr,>#].u2BL.pB```ppp\  %XS*k(--p_][[[YYթSjC||Y,qxP/rN. kvᙋKHqNb׋WVVqqW^YYj!T*e'B\W^AEBFj.gx(44DV %fI/W(.ʺ(CP]mܹ>ʺΝ;Qiinv3 qe(WuBV\Zz6H Q.g2KJJ""" -&B}}Ce[NV4&DƊh]G&I%%zDP?#b9t$8O=l6ƊWd.^,FI*"}܃p8gorRsaY6)9a˕?vǷiiiMN}ª!C?7*jĈ>޽-[{߽ݻwĸmrRsV8 8NV-_O&CBBRupG׎-\'!e׮mxOՖ̙fþu77)t'vk_yyONƢœby~[7h: !2>uB㏍P(l6<"M'F ZZZښ/+\bZ[[E*_ڏ_ իWсa2m9~q>!BO'_/  }B>!BO'_Rvqw\<7`f6[r9M_o,')rp+7Z2󼸄yL&.lj (g+%E+<ðf (ð 8eYB8<"2qx.7%+QPYV -q0lSJ!8'kq)rxIx / 0 PHVedD2LTUU,+I“dU)\w0EQrd1'JcYmͲrIE<4BE[*%Km1dI5 F) q0-&!Ե%{U2 S[klM"HV%˲ Dpy'\ke"ã(ҚUp:@sG }B>!BO'_/  }B>!b;:ֻw/gffJʻwۺRu34sa_ll'3+GFFvU2l6[mK ;ԩSW|'շo_eerwa1dKugZ\\\BB-U[HHHϞ=O.--#K>QVVކCcYgΜؘ_ZRRϻvzW3EQ}IHLY~$?PsFttL߾}NoAϝi9ԧO8z칳gϊ%&&ݓas9Gkn ^Tp+-a#՚W(ʌiÇ+.Ozjx2ɓ4EvKݲ{s Zf%3OSR6By)M˘^WW׮3;9kVys]ޥKL''';ιs_(*,2={xQQaaz$<Y`h JKK!dJKJ.|]O|ԩk BB~&B\^X?ٌq۶n3̈́-[KL_nR?2erYYُt(۶ut:N=_pͲOdϜ13?{1iSӧO3a*JBH=.ydܼy?!E].צ?R}[ JJ6|h/!_R9W!=> +**>?B(R(*BRRRv;qGYlEUUUG\15X։لF{|xC""M<ٹs3fL jR%yyy\G?29kFߠyc6QFqG?B;-m!71mj~~;$,B/9lB9\Z_E=+.<3kOdc<]ȍhkؙ̯o#rrrM~|zW^yEVKq8;ѣNJSF9`BMK7 /:~DVV֘я^|)11q;`~*i%p\oX(8aophfF\@m)**\|Ŕ)SFibپ}ÿ478q?py/;r#ƷW}}*1˺T*k| 6mzᅹuuҧG <8--mS2.(E+ebS(ڵ+&&z&OI6lܸq„uMR /?w]ջw~;LrrUn/Zh٫׼r~jĉu7zc{ѣ>SҵZ c_}Ғ_~Լdx4їo|@iM1pDѸ`BB1 4hEԗ{C;p\￿qӦT*`P(fl+)Bta:d0DFDFBN}vFdXM&ӤIOUVTx>֓z@BBCuK8ӻk_9WGDDnj(.xPE@W'44t!NnGGGu~Ʋ_~Ԥ+/6iZ.&3MS31=H 1NZZĆ1Æ +,,yb_~_߅?^o6[O4u8dz9*"/ºe*lG~r|^ֱ,Bȑ#'L ?99nN l6[uu2d0EQdsKLraJJ !D_'?&"bccÏa'W(j D!C<ӧϜ8ΒVYp޽߯|kCH:ǘa޽n/~Rvq7?_ ŅΝ=8EEΝt̩oXv$KN3*:ZNTԮz}ɟȑ\9kFddʷޖ<|GnaaaÇ_paa:t:'OoV񆆆GǬ]NxPΝ2eWTT eV=z²]fL\~2 %%9,LҥG'5MUU*bc#o<>/>b=&=i .*U1cF1bΟ?ѳM 2l6Wl6l6lߟw[iӧ۷ ?tfy}=wd2uz3jkk֯t:ƌò{n7U>Vt.z9<9kۖh# l۽rgAQԠA&Lou|CӦM:]hXNcYba=ۺfY6}rz.]/_!\\"!^TpͬV3!*,<]t!j cǍ7np=+ { < :X֊+ܯv玭[ /8/<B+6}={ȩ6Tx Ņ kV9~'|׷;!LdYYVywN1}ۄtbƏ?x)'8rHm6۾ZN:_ YIaii OUĒK}Yofo,ŕ<2j(Ն͛?ݶv)K.ѣp{vi:cZ_r7˲Vu޽kVugՈ>}Z=!N董V*--:tȔgO#!BO؎ڗbv8d/' _pQ '_/ '&BȠAZ32q(#_!Dt\<F!f8yBp;"$.u(H(Bbm3f !348.n2T!f(."[EEїNJEAQE'_x7IENDB`prometheus-smokeping-prober-0.7.1/example-rules.yml000066400000000000000000000012311445353333100225020ustar00rootroot00000000000000--- groups: - name: Smokeping interval: 30s rules: - record: instance:smokeping_probe_success:ratio1m expr: increase(smokeping_response_duration_seconds_count[1m]) / increase(smokeping_requests_total[1m]) - record: instance:smokeping_response_duration_seconds:q50 expr: histogram_quantile(0.50, rate(smokeping_response_duration_seconds_bucket[1m])) - record: instance:smokeping_response_duration_seconds:q90 expr: histogram_quantile(0.90, rate(smokeping_response_duration_seconds_bucket[1m])) - record: instance:smokeping_response_duration_seconds:q99 expr: histogram_quantile(0.99, rate(smokeping_response_duration_seconds_bucket[1m])) prometheus-smokeping-prober-0.7.1/go.mod000066400000000000000000000026211445353333100203060ustar00rootroot00000000000000module github.com/superq/smokeping_prober go 1.19 require ( github.com/alecthomas/kingpin/v2 v2.3.2 github.com/go-kit/log v0.2.1 github.com/prometheus-community/pro-bing v0.2.0 github.com/prometheus/client_golang v1.16.0 github.com/prometheus/common v0.44.0 github.com/prometheus/exporter-toolkit v0.10.0 golang.org/x/sync v0.3.0 gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/uuid v1.3.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect golang.org/x/crypto v0.8.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.30.0 // indirect ) prometheus-smokeping-prober-0.7.1/go.sum000066400000000000000000000172601445353333100203400ustar00rootroot00000000000000github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU= github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= 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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 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/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 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.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus-community/pro-bing v0.2.0 h1:hyK7yPFndU3LCDwEQJwPQUCjNkp1DGP/VxyzrWfXZUU= github.com/prometheus-community/pro-bing v0.2.0/go.mod h1:20arNb2S8rNG3EtmjHyZZU92cfbhQx7oCHZ9sulAV+I= github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/exporter-toolkit v0.10.0 h1:yOAzZTi4M22ZzVxD+fhy1URTuNRj/36uQJJ5S8IPza8= github.com/prometheus/exporter-toolkit v0.10.0/go.mod h1:+sVFzuvV5JDyw+Ih6p3zFxZNVnKQa3x5qPmDSiPu4ZY= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v2 v2.2.2/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= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= prometheus-smokeping-prober-0.7.1/main.go000066400000000000000000000163321445353333100204570ustar00rootroot00000000000000// Copyright 2018 Ben Kochie // // 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. package main import ( "errors" "fmt" "net/http" _ "net/http/pprof" "os" "strconv" "strings" "time" "github.com/prometheus-community/pro-bing" "github.com/superq/smokeping_prober/config" "github.com/alecthomas/kingpin/v2" "github.com/go-kit/log" "github.com/go-kit/log/level" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/common/promlog" "github.com/prometheus/common/promlog/flag" "github.com/prometheus/common/version" "github.com/prometheus/exporter-toolkit/web" "github.com/prometheus/exporter-toolkit/web/kingpinflag" "golang.org/x/sync/errgroup" ) var ( // Generated with: prometheus.ExponentialBuckets(0.00005, 2, 20) defaultBuckets = "5e-05,0.0001,0.0002,0.0004,0.0008,0.0016,0.0032,0.0064,0.0128,0.0256,0.0512,0.1024,0.2048,0.4096,0.8192,1.6384,3.2768,6.5536,13.1072,26.2144" logger log.Logger sc = &config.SafeConfig{ C: &config.Config{}, } ) type hostList []string func (h *hostList) Set(value string) error { if value == "" { return fmt.Errorf("'%s' is not valid hostname", value) } *h = append(*h, value) return nil } func (h *hostList) String() string { return "" } func (h *hostList) IsCumulative() bool { return true } func HostList(s kingpin.Settings) (target *[]string) { target = new([]string) s.SetValue((*hostList)(target)) return } func init() { prometheus.MustRegister(version.NewCollector("smokeping_prober")) } func parseBuckets(buckets string) ([]float64, error) { bucketstrings := strings.Split(buckets, ",") bucketlist := make([]float64, len(bucketstrings)) for i := range bucketstrings { value, err := strconv.ParseFloat(bucketstrings[i], 64) if err != nil { return nil, err } bucketlist[i] = value } return bucketlist, nil } func main() { var ( configFile = kingpin.Flag("config.file", "Optional smokeping_prober configuration yaml file.").String() metricsPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String() webConfig = kingpinflag.AddFlags(kingpin.CommandLine, ":9374") buckets = kingpin.Flag("buckets", "A comma delimited list of buckets to use").Default(defaultBuckets).String() factor = kingpin.Flag("native-histogram-factor", "The scaling factor for native histogram buckets").Hidden().Default("1.05").Float() interval = kingpin.Flag("ping.interval", "Ping interval duration").Short('i').Default("1s").Duration() privileged = kingpin.Flag("privileged", "Run in privileged ICMP mode").Default("true").Bool() sizeBytes = kingpin.Flag("ping.size", "Ping packet size in bytes").Short('s').Default("56").Int() hosts = HostList(kingpin.Arg("hosts", "List of hosts to ping")) ) promlogConfig := &promlog.Config{} flag.AddFlags(kingpin.CommandLine, promlogConfig) kingpin.Version(version.Print("smokeping_prober")) kingpin.HelpFlag.Short('h') kingpin.Parse() logger = promlog.New(promlogConfig) level.Info(logger).Log("msg", "Starting smokeping_prober", "version", version.Info()) level.Info(logger).Log("msg", "Build context", "build_context", version.BuildContext()) if *sizeBytes < 24 || *sizeBytes > 65535 { level.Error(logger).Log("msg", "Invalid packet size. (24-65535)", "bytes", *sizeBytes) os.Exit(1) } if err := sc.ReloadConfig(*configFile); err != nil { if errors.Is(err, os.ErrNotExist) { level.Info(logger).Log("msg", "ignoring missing config file", "filename", *configFile) } else { level.Error(logger).Log("msg", "error loading config", "filename", err.Error()) os.Exit(1) } } bucketlist, err := parseBuckets(*buckets) if err != nil { level.Error(logger).Log("msg", "Failed to parse buckets", "err", err) os.Exit(1) } pingResponseSeconds := newPingResponseHistogram(bucketlist, *factor) prometheus.MustRegister(pingResponseSeconds) pingers := make([]*probing.Pinger, len(*hosts)) var pinger *probing.Pinger var host string for i, host := range *hosts { pinger = probing.New(host) err := pinger.Resolve() if err != nil { level.Error(logger).Log("msg", "Failed to resolve pinger", "err", err) os.Exit(1) } level.Info(logger).Log("msg", "Pinger resolved", "host", host, "ip_addr", pinger.IPAddr()) pinger.Interval = *interval pinger.RecordRtts = false pinger.SetPrivileged(*privileged) pinger.Size = *sizeBytes pingers[i] = pinger } maxInterval := *interval sc.Lock() for _, targetGroup := range sc.C.Targets { if targetGroup.Interval > maxInterval { maxInterval = targetGroup.Interval } packetSize := targetGroup.Size if packetSize < 24 || packetSize > 65535 { level.Error(logger).Log("msg", "Invalid packet size. (24-65535)", "bytes", packetSize) os.Exit(1) } for _, host = range targetGroup.Hosts { pinger = probing.New(host) pinger.Interval = targetGroup.Interval pinger.RecordRtts = false pinger.SetNetwork(targetGroup.Network) pinger.Size = packetSize pinger.Source = targetGroup.Source if targetGroup.Protocol == "icmp" { pinger.SetPrivileged(true) } err := pinger.Resolve() if err != nil { level.Error(logger).Log("msg", "failed to resolve pinger", "error", err.Error()) os.Exit(1) } pingers = append(pingers, pinger) } } sc.Unlock() if len(pingers) == 0 { level.Error(logger).Log("msg", "no targets specified on command line or in config file") os.Exit(1) } splay := time.Duration(interval.Nanoseconds() / int64(len(pingers))) level.Info(logger).Log("msg", fmt.Sprintf("Waiting %s between starting pingers", splay)) g := new(errgroup.Group) for _, pinger := range pingers { level.Info(logger).Log("msg", "Starting prober", "address", pinger.Addr(), "interval", pinger.Interval, "size_bytes", pinger.Size, "source", pinger.Source) g.Go(pinger.Run) time.Sleep(splay) } prometheus.MustRegister(NewSmokepingCollector(&pingers, *pingResponseSeconds)) http.Handle(*metricsPath, promhttp.Handler()) if *metricsPath != "/" && *metricsPath != "" { landingConfig := web.LandingConfig{ Name: "Smokeping Prober", Description: "Smokeping-style packet prober for Prometheus", Version: version.Info(), Links: []web.LandingLinks{ { Address: *metricsPath, Text: "Metrics", }, }, } landingPage, err := web.NewLandingPage(landingConfig) if err != nil { level.Error(logger).Log("err", err) os.Exit(1) } http.Handle("/", landingPage) } server := &http.Server{} if err := web.ListenAndServe(server, webConfig, logger); err != nil { level.Error(logger).Log("err", err) os.Exit(1) } for _, pinger := range pingers { pinger.Stop() } if err = g.Wait(); err != nil { level.Error(logger).Log("msg", "pingers failed", "error", err) os.Exit(1) } } prometheus-smokeping-prober-0.7.1/smokeping_prober.yml000066400000000000000000000005771445353333100233000ustar00rootroot00000000000000--- targets: - hosts: - localhost interval: 1s # Duration, Default 1s. network: ip # One of ip, ip4, ip6. Default: ip (automatic IPv4/IPv6) protocol: icmp # One of icmp, udp. Default: icmp (Requires privileged operation) size: 56 # Packet data size in bytes. Default 56 (Range: 24 - 65535) source: 127.0.1.1 # Souce IP address to use. Default: None (automatic selection)