Skip to content

Commit

Permalink
Merge ae80f5f into e355e14
Browse files Browse the repository at this point in the history
  • Loading branch information
talal committed Jan 14, 2020
2 parents e355e14 + ae80f5f commit 1493d6a
Show file tree
Hide file tree
Showing 32 changed files with 1,508 additions and 927 deletions.
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,17 @@ static-check: FORCE
@$(GO) vet $(GO_BUILDFLAGS) $(GO_ALLPKGS)

# detailed unit test run (incl. test coverage)
build/%.cover.out: FORCE build/mock-swift-dispersion-report build/mock-swift-recon
build/%.cover.out: FORCE build/mock-swift-dispersion build/mock-swift-recon
@printf "\e[1;36m>> go test $(subst _,/,$*)\e[0m\n"
$(GO) test $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' -coverprofile=$@ -covermode=count -coverpkg=$(subst $(space),$(comma),$(GO_COVERPKGS)) $(subst _,/,$*)
build/cover.out: $(GO_COVERFILES)
$(GO) run $(GO_BUILDFLAGS) test/cmd/gocovcat/main.go $(GO_COVERFILES) > $@
build/cover.html: build/cover.out
$(GO) tool cover -html $< -o $@

build/mock-swift-dispersion-report: FORCE
build/mock-swift-dispersion: FORCE
$(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-dispersion-report'
$(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-dispersion-report-with-errors'

build/mock-swift-recon: FORCE
$(GO) install $(GO_BUILDFLAGS) -ldflags '$(GO_LDFLAGS)' '$(PKG)/test/cmd/mock-swift-recon'
Expand Down
72 changes: 36 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,42 +55,42 @@ for `swift-health-exporter`.

### Dispersion Report

| Metric | Labels |
| ---------------- | ----------- |
| `swift_dispersion_container_copies_expected` | |
| `swift_dispersion_container_copies_found` | |
| `swift_dispersion_container_copies_missing` | |
| `swift_dispersion_container_overlapping` | |
| `swift_dispersion_object_copies_expected` | |
| `swift_dispersion_object_copies_found` | |
| `swift_dispersion_object_copies_missing` | |
| `swift_dispersion_object_overlapping` | |
| `swift_dispersion_exit_code` | `query` |
| Metric | Labels |
| ---------------- | ----------- |
| `swift_dispersion_container_copies_expected` | |
| `swift_dispersion_container_copies_found` | |
| `swift_dispersion_container_copies_missing` | |
| `swift_dispersion_container_overlapping` | |
| `swift_dispersion_object_copies_expected` | |
| `swift_dispersion_object_copies_found` | |
| `swift_dispersion_object_copies_missing` | |
| `swift_dispersion_object_overlapping` | |
| `swift_dispersion_task_exit_code` | `task`, `failed_query` |

### Recon

| Metric | Labels |
| ---------------- | ----------- |
| `swift_cluster_accounts_quarantined` | `storage_ip` |
| `swift_cluster_accounts_replication_age` | `storage_ip` |
| `swift_cluster_accounts_replication_duration` | `storage_ip` |
| `swift_cluster_containers_quarantined` | `storage_ip` |
| `swift_cluster_containers_replication_age` | `storage_ip` |
| `swift_cluster_containers_replication_duration` | `storage_ip` |
| `swift_cluster_containers_updater_sweep_time` | `storage_ip` |
| `swift_cluster_drives_audit_errors` | `storage_ip` |
| `swift_cluster_drives_unmounted` | `storage_ip` |
| `swift_cluster_md5_all` | `kind` |
| `swift_cluster_md5_errors` | `kind` |
| `swift_cluster_md5_matched` | `kind` |
| `swift_cluster_md5_not_matched` | `kind` |
| `swift_cluster_objects_quarantined` | `storage_ip` |
| `swift_cluster_objects_replication_age` | `storage_ip` |
| `swift_cluster_objects_replication_duration` | `storage_ip` |
| `swift_cluster_objects_updater_sweep_time` | `storage_ip` |
| `swift_cluster_storage_capacity_bytes` | `storage_ip` |
| `swift_cluster_storage_free_bytes` | `storage_ip` |
| `swift_cluster_storage_used_bytes` | `storage_ip` |
| `swift_cluster_storage_used_percent_by_disk` | `storage_ip`, `disk` |
| `swift_cluster_storage_used_percent` | `storage_ip` |
| `swift_recon_exit_code` | `query` |
| Metric | Labels |
| ---------------- | ----------- |
| `swift_cluster_accounts_quarantined` | `storage_ip` |
| `swift_cluster_accounts_replication_age` | `storage_ip` |
| `swift_cluster_accounts_replication_duration` | `storage_ip` |
| `swift_cluster_containers_quarantined` | `storage_ip` |
| `swift_cluster_containers_replication_age` | `storage_ip` |
| `swift_cluster_containers_replication_duration` | `storage_ip` |
| `swift_cluster_containers_updater_sweep_time` | `storage_ip` |
| `swift_cluster_drives_audit_errors` | `storage_ip` |
| `swift_cluster_drives_unmounted` | `storage_ip` |
| `swift_cluster_md5_all` | `kind` |
| `swift_cluster_md5_errors` | `kind` |
| `swift_cluster_md5_matched` | `kind` |
| `swift_cluster_md5_not_matched` | `kind` |
| `swift_cluster_objects_quarantined` | `storage_ip` |
| `swift_cluster_objects_replication_age` | `storage_ip` |
| `swift_cluster_objects_replication_duration` | `storage_ip` |
| `swift_cluster_objects_updater_sweep_time` | `storage_ip` |
| `swift_cluster_storage_capacity_bytes` | `storage_ip` |
| `swift_cluster_storage_free_bytes` | `storage_ip` |
| `swift_cluster_storage_used_bytes` | `storage_ip` |
| `swift_cluster_storage_used_percent_by_disk` | `storage_ip`, `disk` |
| `swift_cluster_storage_used_percent` | `storage_ip` |
| `swift_recon_task_exit_code` | `task`, `failed_query` |
72 changes: 72 additions & 0 deletions collector/collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// 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.

package collector

import (
"fmt"
"strings"

"github.com/prometheus/client_golang/prometheus"
"github.com/sapcc/go-bits/logg"
)

type typedDesc struct {
desc *prometheus.Desc
valueType prometheus.ValueType
}

func (d *typedDesc) mustNewConstMetric(value float64, labels ...string) prometheus.Metric {
return prometheus.MustNewConstMetric(d.desc, d.valueType, value, labels...)
}

func (d *typedDesc) describe(ch chan<- *prometheus.Desc) {
ch <- d.desc
}

// collectorTask is the interface that a specific collector task must implement.
type collectorTask interface {
describeMetrics(chan<- *prometheus.Desc)
collectMetrics(chan<- prometheus.Metric) error
}

// executeTask runs the metric collection for a specific collector task,
// and updates the respective exit code metric.
func executeTask(collectorName, taskName string, task collectorTask, ch chan<- prometheus.Metric, exitCodeTypedDesc typedDesc) {
exitCode := 0
failedQuery := ""

err := task.collectMetrics(ch)
if err != nil {
exitCode = 1
logg.Error("swift %s collector: %s: %s", collectorName, taskName, err.Error())
if e, ok := err.(*taskCollectionError); ok {
failedQuery = strings.Join(e.cmdArgs, " ")
}
}

ch <- exitCodeTypedDesc.mustNewConstMetric(float64(exitCode), taskName, failedQuery)
}

// taskCollectionError is a custom error type that a task.collectMetrics() can
// return in order to provide context about the specific command arguments
// that were used during that collection.
type taskCollectionError struct {
// we are only interested in the arguments, not the command itself
cmdArgs []string
inner error
}

func (e *taskCollectionError) Error() string {
return fmt.Sprintf("%s: %s", strings.Join(e.cmdArgs, " "), e.inner.Error())
}
192 changes: 192 additions & 0 deletions collector/dispersion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Copyright 2019 SAP SE
//
// 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 collector

import (
"encoding/json"
"os/exec"

"github.com/prometheus/client_golang/prometheus"
)

// DispersionCollector implements the prometheus.Collector interface.
type DispersionCollector struct {
taskExitCode typedDesc
dispersionReportDumpTask collectorTask
}

// NewDispersionCollector creates a new DispersionCollector.
func NewDispersionCollector(pathToExecutable string) *DispersionCollector {
return &DispersionCollector{
taskExitCode: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_task_exit_code",
"The exit code for a Swift Dispersion Report query execution.",
[]string{"task", "failed_query"}, nil),
valueType: prometheus.GaugeValue,
},
dispersionReportDumpTask: newDispersionReportDumpTask(pathToExecutable),
}
}

// Describe implements the prometheus.Collector interface.
func (c *DispersionCollector) Describe(ch chan<- *prometheus.Desc) {
c.taskExitCode.describe(ch)
c.dispersionReportDumpTask.describeMetrics(ch)
}

// Collect implements the prometheus.Collector interface.
func (c *DispersionCollector) Collect(ch chan<- prometheus.Metric) {
executeTask("dispersion", "reportDump", c.dispersionReportDumpTask, ch, c.taskExitCode)
}

///////////////////////////////////////////////////////////////////////////////
// Dispersion collector tasks.

// dispersionReportDumpTask implements the collector.collectorTask interface.
type dispersionReportDumpTask struct {
pathToDispersionExecutable string

containerCopiesExpected typedDesc
containerCopiesFound typedDesc
containerCopiesMissing typedDesc
containerOverlapping typedDesc
objectCopiesExpected typedDesc
objectCopiesFound typedDesc
objectCopiesMissing typedDesc
objectOverlapping typedDesc
}

func newDispersionReportDumpTask(pathToDispersionExecutable string) collectorTask {
return &dispersionReportDumpTask{
pathToDispersionExecutable: pathToDispersionExecutable,
containerCopiesExpected: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_container_copies_expected",
"Expected container copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
containerCopiesFound: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_container_copies_found",
"Found container copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
containerCopiesMissing: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_container_copies_missing",
"Missing container copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
containerOverlapping: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_container_overlapping",
"Expected container copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
objectCopiesExpected: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_object_copies_expected",
"Expected object copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
objectCopiesFound: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_object_copies_found",
"Found object copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
objectCopiesMissing: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_object_copies_missing",
"Missing object copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
objectOverlapping: typedDesc{
desc: prometheus.NewDesc(
"swift_dispersion_object_overlapping",
"Expected object copies reported by the swift-dispersion-report tool.",
nil, nil),
valueType: prometheus.GaugeValue,
},
}
}

// dispersionReportDumpTask implements the collector.collectorTask interface.
func (t *dispersionReportDumpTask) describeMetrics(ch chan<- *prometheus.Desc) {
t.containerCopiesExpected.describe(ch)
t.containerCopiesFound.describe(ch)
t.containerCopiesMissing.describe(ch)
t.containerOverlapping.describe(ch)
t.objectCopiesExpected.describe(ch)
t.objectCopiesFound.describe(ch)
t.objectCopiesMissing.describe(ch)
t.objectOverlapping.describe(ch)
}

// dispersionReportDumpTask implements the collector.collectorTask interface.
func (t *dispersionReportDumpTask) collectMetrics(ch chan<- prometheus.Metric) error {
cmdArgs := []string{"--dump-json"}
out, err := exec.Command(t.pathToDispersionExecutable, cmdArgs...).Output()
if err != nil {
return &taskCollectionError{cmdArgs, err}
}

var data struct {
Object struct {
Expected int64 `json:"copies_expected"`
Found int64 `json:"copies_found"`
Overlapping int64 `json:"overlapping"`
Missing int64
} `json:"object"`
Container struct {
Expected int64 `json:"copies_expected"`
Found int64 `json:"copies_found"`
Overlapping int64 `json:"overlapping"`
Missing int64
} `json:"container"`
}
err = json.Unmarshal(out, &data)
if err != nil {
return &taskCollectionError{cmdArgs, err}
}

cntr := data.Container
if cntr.Expected > 0 && cntr.Found > 0 {
cntr.Missing = cntr.Expected - cntr.Found
}
ch <- t.containerCopiesExpected.mustNewConstMetric(float64(cntr.Expected))
ch <- t.containerCopiesFound.mustNewConstMetric(float64(cntr.Found))
ch <- t.containerCopiesMissing.mustNewConstMetric(float64(cntr.Missing))
ch <- t.containerOverlapping.mustNewConstMetric(float64(cntr.Overlapping))

obj := data.Object
if obj.Expected > 0 && obj.Found > 0 {
obj.Missing = obj.Expected - obj.Found
}
ch <- t.objectCopiesExpected.mustNewConstMetric(float64(obj.Expected))
ch <- t.objectCopiesFound.mustNewConstMetric(float64(obj.Found))
ch <- t.objectCopiesMissing.mustNewConstMetric(float64(obj.Missing))
ch <- t.objectOverlapping.mustNewConstMetric(float64(obj.Overlapping))

return nil
}
20 changes: 18 additions & 2 deletions collectors/dispersion_test.go → collector/dispersion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package collectors
package collector

import (
"path/filepath"
Expand All @@ -35,6 +35,22 @@ func TestDispersionCollector(t *testing.T) {
Method: "GET",
Path: "/metrics",
ExpectStatus: 200,
ExpectBody: assert.FixtureFile("fixtures/dispersion_metrics.prom"),
ExpectBody: assert.FixtureFile("fixtures/dispersion_successful_collect.prom"),
}.Check(t, promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
}

func TestDispersionCollectorWithErrors(t *testing.T) {
pathToExecutable, err := filepath.Abs("../build/mock-swift-dispersion-report-with-errors")
if err != nil {
t.Error(err)
}

registry := prometheus.NewPedanticRegistry()
registry.MustRegister(NewDispersionCollector(pathToExecutable))
assert.HTTPRequest{
Method: "GET",
Path: "/metrics",
ExpectStatus: 200,
ExpectBody: assert.FixtureFile("fixtures/dispersion_failed_collect.prom"),
}.Check(t, promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
}
Loading

0 comments on commit 1493d6a

Please sign in to comment.