From 9cda67d44ad87c9df9fe207a53f81a280862ddb9 Mon Sep 17 00:00:00 2001 From: Vaibhav Bhembre Date: Wed, 6 Jan 2016 13:24:20 -0500 Subject: [PATCH] Add Ceph Exporter --- .gitignore | 24 +++ CONTRIBUTING.md | 45 ++++ LICENSE | 201 ++++++++++++++++++ Makefile | 17 ++ Makefile.COMMON | 119 +++++++++++ README.md | 51 +++++ collectors/cluster_usage.go | 169 +++++++++++++++ collectors/cluster_usage_test.go | 69 ++++++ collectors/conn.go | 69 ++++++ collectors/health.go | 235 +++++++++++++++++++++ collectors/health_test.go | 97 +++++++++ collectors/monitors.go | 352 +++++++++++++++++++++++++++++++ collectors/monitors_test.go | 292 +++++++++++++++++++++++++ collectors/pool_usage.go | 185 ++++++++++++++++ collectors/pool_usage_test.go | 73 +++++++ exporter.go | 115 ++++++++++ sample.png | Bin 0 -> 74014 bytes 17 files changed, 2113 insertions(+) create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 Makefile.COMMON create mode 100644 README.md create mode 100644 collectors/cluster_usage.go create mode 100644 collectors/cluster_usage_test.go create mode 100644 collectors/conn.go create mode 100644 collectors/health.go create mode 100644 collectors/health_test.go create mode 100644 collectors/monitors.go create mode 100644 collectors/monitors_test.go create mode 100644 collectors/pool_usage.go create mode 100644 collectors/pool_usage_test.go create mode 100644 exporter.go create mode 100644 sample.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..daf913b --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..68999f7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,45 @@ +Contributing +============ + +Please use this guide before making any contributions to this repository. + +Preliminary +----------- + +* All code **must** be [`gofmt`](https://golang.org/cmd/gofmt/)'d, [`golint`](https://github.com/golang/lint)'d and [`go vet`](https://golang.org/cmd/vet/)'d before being committed. +* Code **should** have test coverage to ensure its correctness. + +PRs +--- + +**Commits** + +Keep individual commits descriptive. Prefix them with the collector name and a +colon. Anyone viewing the git history should be able to determine from those +first 80 characters, the body of the commit. Feel free to expand further on +the commit but keep the first 80 characters on point. + +Good Commit: + +``` +monitor: expose metrics for clock skew + - scrape monitor's skew value from ceph's status +``` + +Bad Commit: + +``` +new monitor metrics +``` + +Use your own discretion when deciding whether or not to squash multiple commits +in a PR to a single commit. However, each commit should contain a single, +logical unit of change, and a descriptive message. + +Resources +--------- + +* [Effective Go](https://golang.org/doc/effective_go.html) +* [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments) +* [How to Write Go Code](https://golang.org/doc/code.html) +* [Twelve Go Best Practices](https://talks.golang.org/2013/bestpractices.slide) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + 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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bfdb9d4 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +# Copyright 2015 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. + +VERSION := 0.1.0 +TARGET := ceph_exporter + +include Makefile.COMMON diff --git a/Makefile.COMMON b/Makefile.COMMON new file mode 100644 index 0000000..d2302dc --- /dev/null +++ b/Makefile.COMMON @@ -0,0 +1,119 @@ +# Copyright 2015 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. + +# THE AUTHORITATIVE VERSION OF THIS MAKEFILE LIVES IN: +# +# https://github.com/prometheus/utils +# +# PLEASE MAKE ANY CHANGES THERE AND PROPAGATE THEM TO ALL PROMETHEUS +# REPOSITORIES THAT ARE USING THIS MAKEFILE. +# +# This file provides common Makefile infrastructure for several Prometheus +# components. This includes make tasks for downloading Go, setting up a +# self-contained build environment, fetching Go dependencies, building +# binaries, running tests, and doing release management. This file is intended +# to be included from a project's Makefile, which needs to define the following +# variables, at a minimum: +# +# * VERSION - The current version of the project in question. +# * TARGET - The desired name of the built binary. +# +# Many of the variables defined below are defined conditionally (using '?'), +# which allows the project's main Makefile to override any of these settings, if +# needed. See also: +# +# https://www.gnu.org/software/make/manual/html_node/Flavors.html#Flavors. +# +# The including Makefile may define any number of extra targets that are +# specific to that project. + +VERSION ?= $(error VERSION not set in including Makefile) +TARGET ?= $(error TARGET not set in including Makefile) + +SRC ?= $(shell find . -type f -name "*.go" ! -path "./.build/*") +GOOS := $(shell uname | tr A-Z a-z) +GOARCH := $(subst x86_64,amd64,$(patsubst i%86,386,$(shell uname -m))) + +ifeq ($(GOOS),darwin) + RELEASE_SUFFIX ?= -osx$(shell sw_vers -productVersion) +endif + +GO_VERSION ?= 1.4.2 + +ifeq ($(shell type go >/dev/null && go version | sed 's/.*go\([0-9.]*\).*/\1/'), $(GO_VERSION)) + GOROOT := $(shell go env GOROOT) +else + GOROOT := $(CURDIR)/.build/go$(GO_VERSION) +endif + +GOURL ?= https://golang.org/dl +GOPKG ?= go$(GO_VERSION).$(GOOS)-$(GOARCH)$(RELEASE_SUFFIX).tar.gz +GOPATH := $(CURDIR)/.build/gopath +GOCC ?= $(GOROOT)/bin/go +GO ?= GOROOT=$(GOROOT) GOPATH=$(GOPATH) $(GOCC) +GOFMT ?= $(GOROOT)/bin/gofmt + +# Never honor GOBIN, should it be set at all. +unexport GOBIN + +SUFFIX ?= $(GOOS)-$(GOARCH) +BINARY ?= $(TARGET) +ARCHIVE ?= $(TARGET)-$(VERSION).$(SUFFIX).tar.gz +ROOTPKG ?= github.com/prometheus/$(TARGET) +SELFLINK ?= $(GOPATH)/src/$(ROOTPKG) + +default: $(BINARY) + +$(GOCC): + @echo Go version $(GO_VERSION) required but not found in PATH. + @echo About to download and install go$(GO_VERSION) to $(GOROOT) + @echo Abort now if you want to manually install it system-wide instead. + @echo + @sleep 5 + mkdir -p $(GOROOT) + curl -L $(GOURL)/$(GOPKG) | tar -C $(GOROOT) --strip 1 -xz + +$(SELFLINK): + mkdir -p $(dir $@) + ln -s $(CURDIR) $@ + +dependencies-stamp: $(GOCC) $(SRC) | $(SELFLINK) + $(GO) get -d + touch $@ + +$(BINARY): $(GOCC) $(SRC) dependencies-stamp Makefile Makefile.COMMON + $(GO) build $(GOFLAGS) -o $@ + +.PHONY: archive +archive: $(ARCHIVE) + +$(ARCHIVE): $(BINARY) + tar -czf $@ $< + +.PHONY: tag +tag: + git tag $(VERSION) + git push --tags + +.PHONY: test +test: $(GOCC) dependencies-stamp + $(GO) test ./... + +.PHONY: format +format: $(GOCC) + find . -iname '*.go' | egrep -v "^\./\.build|./generated|\./Godeps|\.(l|y)\.go" | xargs -n1 $(GOFMT) -w -s=true + +.PHONY: clean +clean: + rm -rf $(BINARY) $(ARCHIVE) .build *-stamp + diff --git a/README.md b/README.md new file mode 100644 index 0000000..f13ecb9 --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# Ceph Exporter +Prometheus exporter that scrapes meta information about a running ceph cluster. All the information gathered from the cluster is done by interacting with the monitors using an appropriate wrapper over `rados_mon_command()`. Hence, no additional setup is necessary other than having a working ceph cluster. + +## Dependencies + +You should ideally run this exporter from the client that can talk to +Ceph. Like any other ceph client it needs the following files to run +correctly. + + * `ceph.conf` containing your ceph configuration. + * `ceph..keyring` in order to authenticate to your cluster. + +Ceph exporter will automatically pick those up if they are present in +any of the [default +locations](http://docs.ceph.com/docs/master/rados/configuration/ceph-conf/#the-configuration-file). Otherwise you will need to provide the configuration manually using `--ceph.config` flag. + +We use Ceph's [official Golang client](https://github.com/ceph/go-ceph) to run commands on the cluster. + +## Flags + +Name | Description | Default +---- | ---- | ---- +telemetry.addr | Host:Port pair to run exporter on | `*:9190` +telemetry.path | URL Path for surfacing metrics to prometheus | `/metrics` +ceph.config | Path to ceph configuration file | "" + +## Installation + +Typical way of installing in Go should work. + +``` +go install +``` + +A Makefile is provided in case you find a need for it. + +## Contributing + +Please refer to the [CONTRIBUTING](CONTRIBUTING.md) guide for more +information on how to submit your changes to this repository. + +## Sample view + +If you have [promdash](https://github.com/prometheus/promdash) set up you +can generate views like: + +![](sample.png) + +--- + +Copyright @ 2016 DigitalOcean™ Inc. diff --git a/collectors/cluster_usage.go b/collectors/cluster_usage.go new file mode 100644 index 0000000..5457958 --- /dev/null +++ b/collectors/cluster_usage.go @@ -0,0 +1,169 @@ +// Copyright 2016 DigitalOcean +// +// 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 collectors + +import ( + "encoding/json" + "log" + + "github.com/prometheus/client_golang/prometheus" +) + +const ( + cephNamespace = "ceph" +) + +// A ClusterUsageCollector is used to gather all the global stats about a given +// ceph cluster. It is sometimes essential to know how fast the cluster is growing +// or shrinking as a whole in order to zero in on the cause. The pool specific +// stats are provided separately. +type ClusterUsageCollector struct { + conn Conn + + // GlobalCapacity displays the total storage capacity of the cluster. This + // information is based on the actual no. of objects that are allocated. It + // does not take overcommittment into consideration. + GlobalCapacity prometheus.Gauge + + // UsedCapacity shows the storage under use. + UsedCapacity prometheus.Gauge + + // AvailableCapacity shows the remaining capacity of the cluster that is left unallocated. + AvailableCapacity prometheus.Gauge + + // Objects show the total no. of RADOS objects that are currently allocated. + Objects prometheus.Gauge +} + +// NewClusterUsageCollector creates and returns the reference to ClusterUsageCollector +// and internally defines each metric that display cluster stats. +func NewClusterUsageCollector(conn Conn) *ClusterUsageCollector { + return &ClusterUsageCollector{ + conn: conn, + + GlobalCapacity: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "cluster_capacity_bytes", + Help: "Total capacity of the cluster", + }), + UsedCapacity: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "cluster_used_bytes", + Help: "Capacity of the cluster currently in use", + }), + AvailableCapacity: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "cluster_available_bytes", + Help: "Available space within the cluster", + }), + Objects: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "cluster_objects", + Help: "No. of rados objects within the cluster", + }), + } +} + +func (c *ClusterUsageCollector) metricsList() []prometheus.Metric { + return []prometheus.Metric{ + c.GlobalCapacity, + c.UsedCapacity, + c.AvailableCapacity, + c.Objects, + } +} + +type cephClusterStats struct { + Stats struct { + TotalBytes json.Number `json:"total_bytes"` + TotalUsedBytes json.Number `json:"total_used_bytes"` + TotalAvailBytes json.Number `json:"total_avail_bytes"` + TotalObjects json.Number `json:"total_objects"` + } `json:"stats"` +} + +func (c *ClusterUsageCollector) collect() error { + cmd := c.cephUsageCommand() + buf, _, err := c.conn.MonCommand(cmd) + if err != nil { + return err + } + + stats := &cephClusterStats{} + if err := json.Unmarshal(buf, stats); err != nil { + return err + } + + tot, err := stats.Stats.TotalBytes.Float64() + if err != nil { + return err + } + c.GlobalCapacity.Set(tot) + + used, err := stats.Stats.TotalUsedBytes.Float64() + if err != nil { + return err + } + c.UsedCapacity.Set(used) + + avail, err := stats.Stats.TotalAvailBytes.Float64() + if err != nil { + return err + } + c.AvailableCapacity.Set(avail) + + objects, err := stats.Stats.TotalObjects.Float64() + if err != nil { + return err + } + c.Objects.Set(objects) + + return nil +} + +func (c *ClusterUsageCollector) cephUsageCommand() []byte { + cmd, err := json.Marshal(map[string]interface{}{ + "prefix": "df", + "detail": "detail", + "format": "json", + }) + if err != nil { + // panic! because ideally in no world this hard-coded input + // should fail. + panic(err) + } + return cmd +} + +// Describe sends the descriptors of each metric over to the provided channel. +// The corresponding metric values are sent separately. +func (c *ClusterUsageCollector) Describe(ch chan<- *prometheus.Desc) { + for _, metric := range c.metricsList() { + ch <- metric.Desc() + } +} + +// Collect sends the metric values for each metric pertaining to the global +// cluster usage over to the provided prometheus Metric channel. +func (c *ClusterUsageCollector) Collect(ch chan<- prometheus.Metric) { + if err := c.collect(); err != nil { + log.Println("failed collecting metrics:", err) + return + } + + for _, metric := range c.metricsList() { + ch <- metric + } +} diff --git a/collectors/cluster_usage_test.go b/collectors/cluster_usage_test.go new file mode 100644 index 0000000..e128f46 --- /dev/null +++ b/collectors/cluster_usage_test.go @@ -0,0 +1,69 @@ +// Copyright 2016 DigitalOcean +// +// 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 collectors + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "regexp" + "testing" + + "github.com/prometheus/client_golang/prometheus" +) + +func TestClusterUsage(t *testing.T) { + var ( + expected = ` +{ + "stats": { + "total_bytes": 10, + "total_used_bytes": 6, + "total_avail_bytes": 4, + "total_objects": 1 + } +}` + ) + + collector := NewClusterUsageCollector(NewNoopConn(expected)) + if err := prometheus.Register(collector); err != nil { + t.Fatalf("collector failed to register: %s", err) + } + + server := httptest.NewServer(prometheus.Handler()) + defer server.Close() + + resp, err := http.Get(server.URL) + if err != nil { + t.Fatalf("unexpected failed response from prometheus: %s", err) + } + defer resp.Body.Close() + + buf, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("failed reading server response: %s", err) + } + + for _, re := range []*regexp.Regexp{ + regexp.MustCompile(`ceph_cluster_capacity_bytes 10`), + regexp.MustCompile(`ceph_cluster_used_bytes 6`), + regexp.MustCompile(`ceph_cluster_available_bytes 4`), + regexp.MustCompile(`ceph_cluster_objects 1`), + } { + if !re.Match(buf) { + t.Errorf("failed matching: %q", re) + } + } +} diff --git a/collectors/conn.go b/collectors/conn.go new file mode 100644 index 0000000..42911e6 --- /dev/null +++ b/collectors/conn.go @@ -0,0 +1,69 @@ +// Copyright 2016 DigitalOcean +// +// 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 collectors + +import "github.com/ceph/go-ceph/rados" + +// Conn interface implements only necessary methods that are used +// in this repository of *rados.Conn. This keeps rest of the implementation +// clean and *rados.Conn doesn't need to show up everywhere (it being +// more of an implementation detail in reality). Also it makes mocking +// easier for unit-testing the collectors. +type Conn interface { + ReadDefaultConfigFile() error + Connect() error + Shutdown() + MonCommand([]byte) ([]byte, string, error) +} + +// Verify that *rados.Conn implements Conn correctly. +var _ Conn = &rados.Conn{} + +// NoopConn is the stub we use for mocking rados Conn. Unit testing +// each individual collectors becomes a lot easier after that. +type NoopConn struct { + output string +} + +// The stub we use for testing should also satisfy the interface properties. +var _ Conn = &NoopConn{} + +// NewNoopConn returns an instance of *NoopConn. The string that we want +// outputted at the end of the command we issue to ceph, should be +// specified in the only input parameter. +func NewNoopConn(output string) *NoopConn { + return &NoopConn{output} +} + +// ReadDefaultConfigFile does not need to return an error. It satisfies +// rados.Conn's function with the same prototype. +func (n *NoopConn) ReadDefaultConfigFile() error { + return nil +} + +// Connect does not need to return an error. It satisfies +// rados.Conn's function with the same prototype. +func (n *NoopConn) Connect() error { + return nil +} + +// Shutdown satisfies rados.Conn's function prototype. +func (n *NoopConn) Shutdown() {} + +// MonCommand returns the provided output string to NoopConn as is, making +// it seem like it actually ran something and produced that string as a result. +func (n *NoopConn) MonCommand(_ []byte) ([]byte, string, error) { + return []byte(n.output), "", nil +} diff --git a/collectors/health.go b/collectors/health.go new file mode 100644 index 0000000..682c3f5 --- /dev/null +++ b/collectors/health.go @@ -0,0 +1,235 @@ +// Copyright 2016 DigitalOcean +// +// 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 collectors + +import ( + "encoding/json" + "log" + "regexp" + "strconv" + + "github.com/prometheus/client_golang/prometheus" +) + +// ClusterHealthCollector collects information about the health of an overall cluster. +// It surfaces changes in the ceph parameters unlike data usage that ClusterUsageCollector +// does. +type ClusterHealthCollector struct { + conn Conn + + DegradedPGs prometheus.Gauge + UncleanPGs prometheus.Gauge + UndersizedPGs prometheus.Gauge + StalePGs prometheus.Gauge + + DegradedObjectsCount prometheus.Gauge + + OSDsDown prometheus.Gauge +} + +// NewClusterHealthCollector creates a new instance of ClusterHealthCollector to collect health +// metrics on. +func NewClusterHealthCollector(conn Conn) *ClusterHealthCollector { + return &ClusterHealthCollector{ + conn: conn, + + DegradedPGs: prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "degraded_pgs", + Help: "No. of PGs in a degraded state", + }, + ), + UncleanPGs: prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "unclean_pgs", + Help: "No. of PGs in an unclean state", + }, + ), + UndersizedPGs: prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "undersized_pgs", + Help: "No. of undersized PGs in the cluster", + }, + ), + StalePGs: prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "stale_pgs", + Help: "No. of stale PGs in the cluster", + }, + ), + DegradedObjectsCount: prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "degraded_objects", + Help: "No. of degraded objects across all PGs", + }, + ), + OSDsDown: prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "osds_down", + Help: "Count of OSDs that are in DOWN state", + }, + ), + } +} + +func (c *ClusterHealthCollector) metricsList() []prometheus.Metric { + return []prometheus.Metric{ + c.DegradedPGs, + c.UncleanPGs, + c.UndersizedPGs, + c.StalePGs, + c.DegradedObjectsCount, + c.OSDsDown, + } +} + +type cephHealthStats struct { + Summary []struct { + Severity string `json:"severity"` + Summary string `json:"summary"` + } `json:"summary"` +} + +func (c *ClusterHealthCollector) collect() error { + cmd := c.cephUsageCommand() + buf, _, err := c.conn.MonCommand(cmd) + if err != nil { + return err + } + + stats := &cephHealthStats{} + if err := json.Unmarshal(buf, stats); err != nil { + return err + } + + for _, metric := range c.metricsList() { + if gauge, ok := metric.(prometheus.Gauge); ok { + gauge.Set(0) + } + } + + if len(stats.Summary) < 1 { + return nil + } + + var ( + degradedRegex = regexp.MustCompile(`([\d]+) pgs degraded`) + uncleanRegex = regexp.MustCompile(`([\d]+) pgs stuck unclean`) + undersizedRegex = regexp.MustCompile(`([\d]+) pgs undersized`) + staleRegex = regexp.MustCompile(`([\d]+) pgs stale`) + degradedObjectsRegex = regexp.MustCompile(`recovery ([\d]+)/([\d]+) objects degraded`) + osdsDownRegex = regexp.MustCompile(`([\d]+)/([\d]+) in osds are down`) + ) + + for _, s := range stats.Summary { + matched := degradedRegex.FindStringSubmatch(s.Summary) + if len(matched) == 2 { + v, err := strconv.Atoi(matched[1]) + if err != nil { + return err + } + c.DegradedPGs.Set(float64(v)) + } + + matched = uncleanRegex.FindStringSubmatch(s.Summary) + if len(matched) == 2 { + v, err := strconv.Atoi(matched[1]) + if err != nil { + return err + } + c.UncleanPGs.Set(float64(v)) + } + + matched = undersizedRegex.FindStringSubmatch(s.Summary) + if len(matched) == 2 { + v, err := strconv.Atoi(matched[1]) + if err != nil { + return err + } + c.UndersizedPGs.Set(float64(v)) + } + + matched = staleRegex.FindStringSubmatch(s.Summary) + if len(matched) == 2 { + v, err := strconv.Atoi(matched[1]) + if err != nil { + return err + } + c.StalePGs.Set(float64(v)) + } + + matched = degradedObjectsRegex.FindStringSubmatch(s.Summary) + if len(matched) == 3 { + v, err := strconv.Atoi(matched[1]) + if err != nil { + return err + } + c.DegradedObjectsCount.Set(float64(v)) + } + + matched = osdsDownRegex.FindStringSubmatch(s.Summary) + if len(matched) == 3 { + v, err := strconv.Atoi(matched[1]) + if err != nil { + return err + } + c.OSDsDown.Set(float64(v)) + } + + } + + return nil +} + +func (c *ClusterHealthCollector) cephUsageCommand() []byte { + cmd, err := json.Marshal(map[string]interface{}{ + "prefix": "health", + "detail": "detail", + "format": "json", + }) + if err != nil { + // panic! because ideally in no world this hard-coded input + // should fail. + panic(err) + } + return cmd +} + +// Describe sends all the descriptions of individual metrics of ClusterHealthCollector +// to the provided prometheus channel. +func (c *ClusterHealthCollector) Describe(ch chan<- *prometheus.Desc) { + for _, metric := range c.metricsList() { + ch <- metric.Desc() + } +} + +// Collect sends all the collected metrics to the provided prometheus channel. +// It requires the caller to handle synchronization. +func (c *ClusterHealthCollector) Collect(ch chan<- prometheus.Metric) { + if err := c.collect(); err != nil { + log.Println("failed collecting metrics:", err) + return + } + + for _, metric := range c.metricsList() { + ch <- metric + } +} diff --git a/collectors/health_test.go b/collectors/health_test.go new file mode 100644 index 0000000..fdbc802 --- /dev/null +++ b/collectors/health_test.go @@ -0,0 +1,97 @@ +// Copyright 2016 DigitalOcean +// +// 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 collectors + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "regexp" + "testing" + + "github.com/prometheus/client_golang/prometheus" +) + +func TestClusterHealthCollector(t *testing.T) { + for _, tt := range []struct { + input string + regexes []*regexp.Regexp + }{ + { + `{"summary": [{"severity": "HEALTH_WARN", "summary": "5 pgs degraded"}]}`, + []*regexp.Regexp{ + regexp.MustCompile(`degraded_pgs 5`), + }, + }, + { + `{"summary": [{"severity": "HEALTH_WARN", "summary": "6 pgs stuck unclean"}]}`, + []*regexp.Regexp{ + regexp.MustCompile(`unclean_pgs 6`), + }, + }, + { + `{"summary": [{"severity": "HEALTH_WARN", "summary": "7 pgs undersized"}]}`, + []*regexp.Regexp{ + regexp.MustCompile(`undersized_pgs 7`), + }, + }, + { + `{"summary": [{"severity": "HEALTH_WARN", "summary": "8 pgs stale"}]}`, + []*regexp.Regexp{ + regexp.MustCompile(`stale_pgs 8`), + }, + }, + { + `{"summary": [{"severity": "HEALTH_WARN", "summary": "recovery 10/20 objects degraded"}]}`, + []*regexp.Regexp{ + regexp.MustCompile(`degraded_objects 10`), + }, + }, + { + `{"summary": [{"severity": "HEALTH_WARN", "summary": "3/20 in osds are down"}]}`, + []*regexp.Regexp{ + regexp.MustCompile(`osds_down 3`), + }, + }, + } { + func() { + collector := NewClusterHealthCollector(NewNoopConn(tt.input)) + if err := prometheus.Register(collector); err != nil { + t.Fatalf("collector failed to register: %s", err) + } + defer prometheus.Unregister(collector) + + server := httptest.NewServer(prometheus.Handler()) + defer server.Close() + + resp, err := http.Get(server.URL) + if err != nil { + t.Fatalf("unexpected failed response from prometheus: %s", err) + } + defer resp.Body.Close() + + buf, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("failed reading server response: %s", err) + } + + for _, re := range tt.regexes { + if !re.Match(buf) { + t.Errorf("failed matching: %q", re) + } + } + }() + } +} diff --git a/collectors/monitors.go b/collectors/monitors.go new file mode 100644 index 0000000..a216be2 --- /dev/null +++ b/collectors/monitors.go @@ -0,0 +1,352 @@ +// Copyright 2016 DigitalOcean +// +// 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 collectors + +import ( + "encoding/json" + "log" + + "github.com/prometheus/client_golang/prometheus" +) + +// MonitorCollector is used to extract stats related to monitors +// running within Ceph cluster. As we extract information pertaining +// to each monitor instance, there are various vector metrics we +// need to use. +type MonitorCollector struct { + conn Conn + + // TotalKBs display the total storage a given monitor node has. + TotalKBs *prometheus.GaugeVec + + // UsedKBs depict how much of the total storage our monitor process + // has utilized. + UsedKBs *prometheus.GaugeVec + + // AvailKBs shows the space left unused. + AvailKBs *prometheus.GaugeVec + + // PercentAvail shows the amount of unused space as a percentage of total + // space. + PercentAvail *prometheus.GaugeVec + + // Store exposes information about internal backing store. + Store Store + + // ClockSkew shows how far the monitor clocks have skewed from each other. This + // is an important metric because the functioning of Ceph's paxos depends on + // the clocks being aligned as close to each other as possible. + ClockSkew *prometheus.GaugeVec + + // Latency displays the time the monitors take to communicate between themselves. + Latency *prometheus.GaugeVec + + // NodesinQuorum show the size of the working monitor quorum. Any change in this + // metric can imply a significant issue in the cluster if it is not manually changed. + NodesinQuorum prometheus.Gauge +} + +// Store displays information about Monitor's FileStore. It is responsible for +// storing all the meta information about the cluster, including monmaps, osdmaps, +// pgmaps, etc. along with logs and other data. +type Store struct { + // TotalBytes displays the current size of the FileStore. + TotalBytes *prometheus.GaugeVec + + // SSTBytes shows the amount used by LevelDB's sorted-string tables. + SSTBytes *prometheus.GaugeVec + + // LogBytes shows the amount used by logs. + LogBytes *prometheus.GaugeVec + + // MiscBytes shows the amount used by miscellaneous information. + MiscBytes *prometheus.GaugeVec +} + +// NewMonitorCollector creates an instance of the MonitorCollector and instantiates +// the individual metrics that show information about the monitor processes. +func NewMonitorCollector(conn Conn) *MonitorCollector { + return &MonitorCollector{ + conn: conn, + + TotalKBs: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "monitor_capacity_bytes", + Help: "Total storage capacity of the monitor node", + }, + []string{"monitor"}, + ), + UsedKBs: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "monitor_used_bytes", + Help: "Storage of the monitor node that is currently allocated for use", + }, + []string{"monitor"}, + ), + AvailKBs: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "monitor_avail_bytes", + Help: "Total unused storage capacity that the monitor node has left", + }, + []string{"monitor"}, + ), + PercentAvail: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "monitor_avail_percent", + Help: "Percentage of total unused storage capacity that the monitor node has left", + }, + []string{"monitor"}, + ), + Store: Store{ + TotalBytes: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "monitor_store_capacity_bytes", + Help: "Total capacity of the FileStore backing the monitor daemon", + }, + []string{"monitor"}, + ), + SSTBytes: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "monitor_store_sst_bytes", + Help: "Capacity of the FileStore used only for raw SSTs", + }, + []string{"monitor"}, + ), + LogBytes: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "monitor_store_log_bytes", + Help: "Capacity of the FileStore used only for logging", + }, + []string{"monitor"}, + ), + MiscBytes: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "monitor_store_misc_bytes", + Help: "Capacity of the FileStore used only for storing miscellaneous information", + }, + []string{"monitor"}, + ), + }, + ClockSkew: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "monitor_clock_skew_seconds", + Help: "Clock skew the monitor node is incurring", + }, + []string{"monitor"}, + ), + Latency: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "monitor_latency_seconds", + Help: "Latency the monitor node is incurring", + }, + []string{"monitor"}, + ), + NodesinQuorum: prometheus.NewGauge( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "monitor_quorum_count", + Help: "The total size of the monitor quorum", + }, + ), + } +} + +func (m *MonitorCollector) collectorList() []prometheus.Collector { + return []prometheus.Collector{ + m.TotalKBs, + m.UsedKBs, + m.AvailKBs, + m.PercentAvail, + + m.Store.TotalBytes, + m.Store.SSTBytes, + m.Store.LogBytes, + m.Store.MiscBytes, + + m.ClockSkew, + m.Latency, + } +} + +func (m *MonitorCollector) metricsList() []prometheus.Metric { + return []prometheus.Metric{ + m.NodesinQuorum, + } +} + +type cephMonitorStats struct { + Health struct { + Health struct { + HealthServices []struct { + Mons []struct { + Name string `json:"name"` + KBTotal json.Number `json:"kb_total"` + KBUsed json.Number `json:"kb_used"` + KBAvail json.Number `json:"kb_avail"` + AvailPercent json.Number `json:"avail_percent"` + StoreStats struct { + BytesTotal json.Number `json:"bytes_total"` + BytesSST json.Number `json:"bytes_sst"` + BytesLog json.Number `json:"bytes_log"` + BytesMisc json.Number `json:"bytes_misc"` + } `json:"store_stats"` + } `json:"mons"` + } `json:"health_services"` + } `json:"health"` + TimeChecks struct { + Mons []struct { + Name string `json:"name"` + Skew json.Number `json:"skew"` + Latency json.Number `json:"latency"` + } `json:"mons"` + } `json:"timechecks"` + } `json:"health"` + Quorum []int `json:"quorum"` +} + +func (m *MonitorCollector) collect() error { + cmd := m.cephUsageCommand() + buf, _, err := m.conn.MonCommand(cmd) + if err != nil { + return err + } + + stats := &cephMonitorStats{} + if err := json.Unmarshal(buf, stats); err != nil { + return err + } + + for _, healthService := range stats.Health.Health.HealthServices { + for _, monstat := range healthService.Mons { + kbTotal, err := monstat.KBTotal.Float64() + if err != nil { + return err + } + m.TotalKBs.WithLabelValues(monstat.Name).Set(kbTotal * 1e3) + + kbUsed, err := monstat.KBUsed.Float64() + if err != nil { + return err + } + m.UsedKBs.WithLabelValues(monstat.Name).Set(kbUsed * 1e3) + + kbAvail, err := monstat.KBAvail.Float64() + if err != nil { + return err + } + m.AvailKBs.WithLabelValues(monstat.Name).Set(kbAvail * 1e3) + + percentAvail, err := monstat.AvailPercent.Float64() + if err != nil { + return err + } + m.PercentAvail.WithLabelValues(monstat.Name).Set(percentAvail) + + storeBytes, err := monstat.StoreStats.BytesTotal.Float64() + if err != nil { + return err + } + m.Store.TotalBytes.WithLabelValues(monstat.Name).Set(storeBytes) + + sstBytes, err := monstat.StoreStats.BytesSST.Float64() + if err != nil { + return err + } + m.Store.SSTBytes.WithLabelValues(monstat.Name).Set(sstBytes) + + logBytes, err := monstat.StoreStats.BytesLog.Float64() + if err != nil { + return err + } + m.Store.LogBytes.WithLabelValues(monstat.Name).Set(logBytes) + + miscBytes, err := monstat.StoreStats.BytesMisc.Float64() + if err != nil { + return err + } + m.Store.MiscBytes.WithLabelValues(monstat.Name).Set(miscBytes) + } + } + + for _, monstat := range stats.Health.TimeChecks.Mons { + skew, err := monstat.Skew.Float64() + if err != nil { + return err + } + m.ClockSkew.WithLabelValues(monstat.Name).Set(skew) + + latency, err := monstat.Latency.Float64() + if err != nil { + return err + } + m.Latency.WithLabelValues(monstat.Name).Set(latency) + } + + m.NodesinQuorum.Set(float64(len(stats.Quorum))) + + return nil +} + +func (m *MonitorCollector) cephUsageCommand() []byte { + cmd, err := json.Marshal(map[string]interface{}{ + "prefix": "status", + "format": "json", + }) + if err != nil { + // panic! because ideally in no world this hard-coded input + // should fail. + panic(err) + } + return cmd +} + +// Describe sends the descriptors of each Monitor related metric we have defined +// to the channel provided. +func (m *MonitorCollector) Describe(ch chan<- *prometheus.Desc) { + for _, metric := range m.collectorList() { + metric.Describe(ch) + } + + for _, metric := range m.metricsList() { + ch <- metric.Desc() + } +} + +// Collect extracts the given metrics from the Monitors and sends it to the prometheus +// channel. +func (m *MonitorCollector) Collect(ch chan<- prometheus.Metric) { + if err := m.collect(); err != nil { + log.Println("failed collecting metrics:", err) + return + } + + for _, metric := range m.collectorList() { + metric.Collect(ch) + } + + for _, metric := range m.metricsList() { + ch <- metric + } +} diff --git a/collectors/monitors_test.go b/collectors/monitors_test.go new file mode 100644 index 0000000..b4c9bd7 --- /dev/null +++ b/collectors/monitors_test.go @@ -0,0 +1,292 @@ +// Copyright 2016 DigitalOcean +// +// 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 collectors + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "regexp" + "testing" + + "github.com/prometheus/client_golang/prometheus" +) + +func TestMonitorCollector(t *testing.T) { + for _, tt := range []struct { + input string + regexes []*regexp.Regexp + }{ + { + ` +{ + "health": { + "health": { + "health_services": [ + { + "mons": [ + { + "name": "test-mon01", + "kb_total": 412718256, + "kb_used": 1812852, + "kb_avail": 389917500, + "avail_percent": 94, + "last_updated": "2015-12-28 15:54:03.763348", + "store_stats": { + "bytes_total": 1781282079, + "bytes_sst": 1, + "bytes_log": 609694, + "bytes_misc": 1780672385, + "last_updated": "0.000000" + }, + "health": "HEALTH_OK" + }, + { + "name": "test-mon02", + "kb_total": 412718256, + "kb_used": 1875304, + "kb_avail": 389855048, + "avail_percent": 94, + "last_updated": "2015-12-28 15:53:53.808657", + "store_stats": { + "bytes_total": 1844348214, + "bytes_sst": 2, + "bytes_log": 871605, + "bytes_misc": 1843476609, + "last_updated": "0.000000" + }, + "health": "HEALTH_OK" + }, + { + "name": "test-mon03", + "kb_total": 412718256, + "kb_used": 2095356, + "kb_avail": 389634996, + "avail_percent": 94, + "last_updated": "2015-12-28 15:53:06.292749", + "store_stats": { + "bytes_total": 2069468587, + "bytes_sst": 3, + "bytes_log": 871605, + "bytes_misc": 2068596982, + "last_updated": "0.000000" + }, + "health": "HEALTH_OK" + }, + { + "name": "test-mon04", + "kb_total": 412718256, + "kb_used": 1726276, + "kb_avail": 390004076, + "avail_percent": 94, + "last_updated": "2015-12-28 15:53:10.770775", + "store_stats": { + "bytes_total": 1691972147, + "bytes_sst": 4, + "bytes_log": 871605, + "bytes_misc": 1691100542, + "last_updated": "0.000000" + }, + "health": "HEALTH_OK" + }, + { + "name": "test-mon05", + "kb_total": 412718256, + "kb_used": 1883228, + "kb_avail": 389847124, + "avail_percent": 94, + "last_updated": "2015-12-28 15:53:11.407033", + "store_stats": { + "bytes_total": 1852485942, + "bytes_sst": 5, + "bytes_log": 871605, + "bytes_misc": 1851614337, + "last_updated": "0.000000" + }, + "health": "HEALTH_OK" + } + ] + } + ] + }, + "timechecks": { + "epoch": 70, + "round": 3362, + "round_status": "finished", + "mons": [ + { + "name": "test-mon01", + "skew": 0.000000, + "latency": 0.000000, + "health": "HEALTH_OK" + }, + { + "name": "test-mon02", + "skew": -0.000002, + "latency": 0.000815, + "health": "HEALTH_OK" + }, + { + "name": "test-mon03", + "skew": -0.000002, + "latency": 0.000829, + "health": "HEALTH_OK" + }, + { + "name": "test-mon04", + "skew": -0.000019, + "latency": 0.000609, + "health": "HEALTH_OK" + }, + { + "name": "test-mon05", + "skew": -0.000628, + "latency": 0.000659, + "health": "HEALTH_OK" + } + ] + }, + "summary": [], + "overall_status": "HEALTH_OK", + "detail": [] + }, + "fsid": "6C9BF03E-044E-4EEB-9C5F-145A54ECF7DB", + "election_epoch": 70, + "quorum": [ + 0, + 1, + 2, + 3, + 4 + ], + "monmap": { + "epoch": 12, + "fsid": "6C9BF03E-044E-4EEB-9C5F-145A54ECF7DB", + "modified": "2015-11-25 07:58:56.388352", + "created": "0.000000", + "mons": [ + { + "rank": 0, + "name": "test-mon01", + "addr": "10.123.1.25:6789\/0" + }, + { + "rank": 1, + "name": "test-mon02", + "addr": "10.123.1.26:6789\/0" + }, + { + "rank": 2, + "name": "test-mon03", + "addr": "10.123.2.25:6789\/0" + }, + { + "rank": 3, + "name": "test-mon04", + "addr": "10.123.2.26:6789\/0" + }, + { + "rank": 4, + "name": "test-mon05", + "addr": "10.123.2.27:6789\/0" + } + ] + } +} +`, + []*regexp.Regexp{ + regexp.MustCompile(`ceph_monitor_avail_bytes{monitor="test-mon01"} 3.899175e`), + regexp.MustCompile(`ceph_monitor_avail_bytes{monitor="test-mon02"} 3.89855048e`), + regexp.MustCompile(`ceph_monitor_avail_bytes{monitor="test-mon03"} 3.89634996e`), + regexp.MustCompile(`ceph_monitor_avail_bytes{monitor="test-mon04"} 3.90004076e`), + regexp.MustCompile(`ceph_monitor_avail_bytes{monitor="test-mon05"} 3.89847124e`), + regexp.MustCompile(`ceph_monitor_avail_percent{monitor="test-mon01"} 94`), + regexp.MustCompile(`ceph_monitor_avail_percent{monitor="test-mon02"} 94`), + regexp.MustCompile(`ceph_monitor_avail_percent{monitor="test-mon03"} 94`), + regexp.MustCompile(`ceph_monitor_avail_percent{monitor="test-mon04"} 94`), + regexp.MustCompile(`ceph_monitor_avail_percent{monitor="test-mon05"} 94`), + regexp.MustCompile(`ceph_monitor_clock_skew_seconds{monitor="test-mon01"} 0`), + regexp.MustCompile(`ceph_monitor_clock_skew_seconds{monitor="test-mon02"} -2e-06`), + regexp.MustCompile(`ceph_monitor_clock_skew_seconds{monitor="test-mon03"} -2e-06`), + regexp.MustCompile(`ceph_monitor_clock_skew_seconds{monitor="test-mon04"} -1.9e-05`), + regexp.MustCompile(`ceph_monitor_clock_skew_seconds{monitor="test-mon05"} -0.000628`), + regexp.MustCompile(`ceph_monitor_latency_seconds{monitor="test-mon01"} 0`), + regexp.MustCompile(`ceph_monitor_latency_seconds{monitor="test-mon02"} 0.000815`), + regexp.MustCompile(`ceph_monitor_latency_seconds{monitor="test-mon03"} 0.000829`), + regexp.MustCompile(`ceph_monitor_latency_seconds{monitor="test-mon04"} 0.000609`), + regexp.MustCompile(`ceph_monitor_latency_seconds{monitor="test-mon05"} 0.000659`), + regexp.MustCompile(`ceph_monitor_quorum_count 5`), + regexp.MustCompile(`ceph_monitor_store_log_bytes{monitor="test-mon01"} 609694`), + regexp.MustCompile(`ceph_monitor_store_log_bytes{monitor="test-mon02"} 871605`), + regexp.MustCompile(`ceph_monitor_store_log_bytes{monitor="test-mon03"} 871605`), + regexp.MustCompile(`ceph_monitor_store_log_bytes{monitor="test-mon04"} 871605`), + regexp.MustCompile(`ceph_monitor_store_log_bytes{monitor="test-mon05"} 871605`), + regexp.MustCompile(`ceph_monitor_store_misc_bytes{monitor="test-mon01"} 1.780672385e`), + regexp.MustCompile(`ceph_monitor_store_misc_bytes{monitor="test-mon02"} 1.843476609e`), + regexp.MustCompile(`ceph_monitor_store_misc_bytes{monitor="test-mon03"} 2.068596982e`), + regexp.MustCompile(`ceph_monitor_store_misc_bytes{monitor="test-mon04"} 1.691100542e`), + regexp.MustCompile(`ceph_monitor_store_misc_bytes{monitor="test-mon05"} 1.851614337e`), + regexp.MustCompile(`ceph_monitor_store_sst_bytes{monitor="test-mon01"} 1`), + regexp.MustCompile(`ceph_monitor_store_sst_bytes{monitor="test-mon02"} 2`), + regexp.MustCompile(`ceph_monitor_store_sst_bytes{monitor="test-mon03"} 3`), + regexp.MustCompile(`ceph_monitor_store_sst_bytes{monitor="test-mon04"} 4`), + regexp.MustCompile(`ceph_monitor_store_sst_bytes{monitor="test-mon05"} 5`), + regexp.MustCompile(`ceph_monitor_store_capacity_bytes{monitor="test-mon01"} 1.781282079e`), + regexp.MustCompile(`ceph_monitor_store_capacity_bytes{monitor="test-mon02"} 1.844348214e`), + regexp.MustCompile(`ceph_monitor_store_capacity_bytes{monitor="test-mon03"} 2.069468587e`), + regexp.MustCompile(`ceph_monitor_store_capacity_bytes{monitor="test-mon04"} 1.691972147e`), + regexp.MustCompile(`ceph_monitor_store_capacity_bytes{monitor="test-mon05"} 1.852485942e`), + regexp.MustCompile(`ceph_monitor_capacity_bytes{monitor="test-mon01"} 4.12718256e`), + regexp.MustCompile(`ceph_monitor_capacity_bytes{monitor="test-mon02"} 4.12718256e`), + regexp.MustCompile(`ceph_monitor_capacity_bytes{monitor="test-mon03"} 4.12718256e`), + regexp.MustCompile(`ceph_monitor_capacity_bytes{monitor="test-mon04"} 4.12718256e`), + regexp.MustCompile(`ceph_monitor_capacity_bytes{monitor="test-mon05"} 4.12718256e`), + regexp.MustCompile(`ceph_monitor_used_bytes{monitor="test-mon01"} 1.812852e`), + regexp.MustCompile(`ceph_monitor_used_bytes{monitor="test-mon02"} 1.875304e`), + regexp.MustCompile(`ceph_monitor_used_bytes{monitor="test-mon03"} 2.095356e`), + regexp.MustCompile(`ceph_monitor_used_bytes{monitor="test-mon04"} 1.726276e`), + regexp.MustCompile(`ceph_monitor_used_bytes{monitor="test-mon05"} 1.883228e`), + }, + }, + } { + func() { + collector := NewMonitorCollector(NewNoopConn(tt.input)) + if err := prometheus.Register(collector); err != nil { + t.Fatalf("collector failed to register: %s", err) + } + defer prometheus.Unregister(collector) + + server := httptest.NewServer(prometheus.Handler()) + defer server.Close() + + resp, err := http.Get(server.URL) + if err != nil { + t.Fatalf("unexpected failed response from prometheus: %s", err) + } + defer resp.Body.Close() + + buf, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("failed reading server response: %s", err) + } + + for _, re := range tt.regexes { + if !re.Match(buf) { + t.Errorf("failed matching: %q", re) + } + } + }() + } +} diff --git a/collectors/pool_usage.go b/collectors/pool_usage.go new file mode 100644 index 0000000..b9ea13e --- /dev/null +++ b/collectors/pool_usage.go @@ -0,0 +1,185 @@ +// Copyright 2016 DigitalOcean +// +// 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 collectors + +import ( + "encoding/json" + "errors" + "log" + + "github.com/prometheus/client_golang/prometheus" +) + +// PoolUsageCollector displays statistics about each pool we have created +// in the ceph cluster. +type PoolUsageCollector struct { + conn Conn + + // UsedBytes tracks the amount of bytes currently allocated for the pool. This + // does not factor in the overcommitment made for individual images. + UsedBytes *prometheus.GaugeVec + + // Objects shows the no. of RADOS objects created within the pool. + Objects *prometheus.GaugeVec + + // ReadIO tracks the read IO calls made for the images within each pool. + ReadIO *prometheus.CounterVec + + // WriteIO tracks the write IO calls made for the images within each pool. + WriteIO *prometheus.CounterVec +} + +// NewPoolUsageCollector creates a new instance of PoolUsageCollector and returns +// its reference. +func NewPoolUsageCollector(conn Conn) *PoolUsageCollector { + return &PoolUsageCollector{ + conn: conn, + + UsedBytes: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "pool_used_bytes", + Help: "Capacity of the pool that is currently under use", + }, + []string{"pool"}, + ), + Objects: prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: cephNamespace, + Name: "pool_objects_total", + Help: "Total no. of objects allocated within the pool", + }, + []string{"pool"}, + ), + ReadIO: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: cephNamespace, + Name: "pool_read_total", + Help: "Total read i/o calls the pool has been subject to", + }, + []string{"pool"}, + ), + WriteIO: prometheus.NewCounterVec( + prometheus.CounterOpts{ + Namespace: cephNamespace, + Name: "pool_write_total", + Help: "Total write i/o calls the pool has been subject to", + }, + []string{"pool"}, + ), + } +} + +func (p *PoolUsageCollector) collectorList() []prometheus.Collector { + return []prometheus.Collector{ + p.UsedBytes, + p.Objects, + p.ReadIO, + p.WriteIO, + } +} + +type cephPoolStats struct { + Pools []struct { + Name string `json:"name"` + ID int `json:"id"` + Stats struct { + BytesUsed json.Number `json:"bytes_used"` + Objects json.Number `json:"objects"` + Read json.Number `json:"rd"` + Write json.Number `json:"wr"` + } `json:"stats"` + } `json:"pools"` +} + +func (p *PoolUsageCollector) collect() error { + cmd := p.cephUsageCommand() + buf, _, err := p.conn.MonCommand(cmd) + if err != nil { + return err + } + + stats := &cephPoolStats{} + if err := json.Unmarshal(buf, stats); err != nil { + return err + } + + if len(stats.Pools) < 1 { + return errors.New("no pools found in the cluster to report stats on") + } + + for _, pool := range stats.Pools { + bytesUsed, err := pool.Stats.BytesUsed.Float64() + if err != nil { + return err + } + p.UsedBytes.WithLabelValues(pool.Name).Set(bytesUsed) + + objects, err := pool.Stats.Objects.Float64() + if err != nil { + return err + } + p.Objects.WithLabelValues(pool.Name).Set(objects) + + read, err := pool.Stats.Read.Float64() + if err != nil { + return err + } + p.ReadIO.WithLabelValues(pool.Name).Set(read) + + write, err := pool.Stats.Write.Float64() + if err != nil { + return err + } + p.WriteIO.WithLabelValues(pool.Name).Set(write) + } + + return nil +} + +func (p *PoolUsageCollector) cephUsageCommand() []byte { + cmd, err := json.Marshal(map[string]interface{}{ + "prefix": "df", + "detail": "detail", + "format": "json", + }) + if err != nil { + // panic! because ideally in no world this hard-coded input + // should fail. + panic(err) + } + return cmd +} + +// Describe fulfills the prometheus.Collector's interface and sends the descriptors +// of pool's metrics to the given channel. +func (p *PoolUsageCollector) Describe(ch chan<- *prometheus.Desc) { + for _, metric := range p.collectorList() { + metric.Describe(ch) + } +} + +// Collect extracts the current values of all the metrics and sends them to the +// prometheus channel. +func (p *PoolUsageCollector) Collect(ch chan<- prometheus.Metric) { + if err := p.collect(); err != nil { + log.Println("failed collecting metrics:", err) + return + } + + for _, metric := range p.collectorList() { + metric.Collect(ch) + } +} diff --git a/collectors/pool_usage_test.go b/collectors/pool_usage_test.go new file mode 100644 index 0000000..a95cefa --- /dev/null +++ b/collectors/pool_usage_test.go @@ -0,0 +1,73 @@ +// Copyright 2016 DigitalOcean +// +// 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 collectors + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "regexp" + "testing" + + "github.com/prometheus/client_golang/prometheus" +) + +func TestPoolUsageCollector(t *testing.T) { + for _, tt := range []struct { + input string + regexes []*regexp.Regexp + }{ + { + ` +{"pools": [ + {"name": "rbd", "id": 11, "stats": {"bytes_used": 20, "objects": 5, "rd": 4, "wr": 6}} +]}`, + []*regexp.Regexp{ + regexp.MustCompile(`pool_used_bytes{pool="rbd"} 20`), + regexp.MustCompile(`pool_objects_total{pool="rbd"} 5`), + regexp.MustCompile(`pool_read_total{pool="rbd"} 4`), + regexp.MustCompile(`pool_write_total{pool="rbd"} 6`), + }, + }, + } { + func() { + collector := NewPoolUsageCollector(NewNoopConn(tt.input)) + if err := prometheus.Register(collector); err != nil { + t.Fatalf("collector failed to register: %s", err) + } + defer prometheus.Unregister(collector) + + server := httptest.NewServer(prometheus.Handler()) + defer server.Close() + + resp, err := http.Get(server.URL) + if err != nil { + t.Fatalf("unexpected failed response from prometheus: %s", err) + } + defer resp.Body.Close() + + buf, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("failed reading server response: %s", err) + } + + for _, re := range tt.regexes { + if !re.Match(buf) { + t.Errorf("failed matching: %q", re) + } + } + }() + } +} diff --git a/exporter.go b/exporter.go new file mode 100644 index 0000000..2418b7a --- /dev/null +++ b/exporter.go @@ -0,0 +1,115 @@ +// Copyright 2016 DigitalOcean +// +// 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 ( + "flag" + "log" + "net/http" + "sync" + + ceph_collectors "github.com/digitalocean/ceph_exporter/collectors" + + "github.com/ceph/go-ceph/rados" + "github.com/prometheus/client_golang/prometheus" +) + +// CephExporter wraps all the ceph collectors and provides a single global +// exporter to extracts metrics out of. It also ensures that the collection +// is done in a thread-safe manner, the necessary requirement stated by +// prometheus. It also implements a prometheus.Collector interface in order +// to register it correctly. +type CephExporter struct { + mu sync.Mutex + collectors []prometheus.Collector +} + +// Verify that the exporter implements the interface correctly. +var _ prometheus.Collector = &CephExporter{} + +// NewCephExporter creates an instance to CephExporter and returns a reference +// to it. We can choose to enable a collector to extract stats out of by adding +// it to the list of collectors. +func NewCephExporter(conn *rados.Conn) *CephExporter { + return &CephExporter{ + collectors: []prometheus.Collector{ + ceph_collectors.NewClusterUsageCollector(conn), + ceph_collectors.NewPoolUsageCollector(conn), + ceph_collectors.NewClusterHealthCollector(conn), + ceph_collectors.NewMonitorCollector(conn), + }, + } +} + +// Describe sends all the descriptors of the collectors included to +// the provided channel. +func (c *CephExporter) Describe(ch chan<- *prometheus.Desc) { + for _, cc := range c.collectors { + cc.Describe(ch) + } +} + +// Collect sends the collected metrics from each of the collectors to +// prometheus. Collect could be called several times concurrently +// and thus its run is protected by a single mutex. +func (c *CephExporter) Collect(ch chan<- prometheus.Metric) { + c.mu.Lock() + defer c.mu.Unlock() + + for _, cc := range c.collectors { + cc.Collect(ch) + } +} + +func main() { + var ( + addr = flag.String("telemetry.addr", ":9190", "host:port for ceph exporter") + metricsPath = flag.String("telemetry.path", "/metrics", "URL path for surfacing collected metrics") + + cephConfig = flag.String("ceph.config", "", "path to ceph config file") + ) + flag.Parse() + + conn, err := rados.NewConn() + if err != nil { + log.Fatalf("cannot create new ceph connection: %s", err) + } + + if *cephConfig != "" { + err = conn.ReadConfigFile(*cephConfig) + } else { + err = conn.ReadDefaultConfigFile() + } + if err != nil { + log.Fatalf("cannot read ceph config file: %s", err) + } + + if err := conn.Connect(); err != nil { + log.Fatalf("cannot connect to ceph cluster: %s", err) + } + defer conn.Shutdown() + + prometheus.MustRegister(NewCephExporter(conn)) + + http.Handle(*metricsPath, prometheus.Handler()) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, *metricsPath, http.StatusMovedPermanently) + }) + + log.Printf("Starting ceph exporter on %q", *addr) + if err := http.ListenAndServe(*addr, nil); err != nil { + log.Fatalf("cannot start ceph exporter: %s", err) + } +} diff --git a/sample.png b/sample.png new file mode 100644 index 0000000000000000000000000000000000000000..21418be649b7e27e5ef7a730ce874516658fb2ea GIT binary patch literal 74014 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 IjV&qhU;Or0Z2$lO literal 0 HcmV?d00001