node_exporter/collector/mdadm_linux.go
Pranshu Srivastava 0c10545e8d
Some checks failed
bsd / Run end-to-end tests on FreeBSD (push) Has been cancelled
bsd / Run end-to-end tests on OpenBSD (push) Has been cancelled
bsd / Run end-to-end tests on NetBSD (push) Has been cancelled
bsd / Run end-to-end tests on DragonFly (push) Has been cancelled
bsd / Run end-to-end tests on Solaris (push) Has been cancelled
bsd / Run end-to-end tests on macOS (push) Has been cancelled
golangci-lint / lint (push) Has been cancelled
collector/mdadm: Support RAID metrics (#3031)
Use `sysfs` for RAID stats.

Signed-off-by: Pranshu Srivastava <rexagod@gmail.com>
2025-02-24 10:19:48 +01:00

250 lines
5.8 KiB
Go

// 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.
//go:build !nomdadm
// +build !nomdadm
package collector
import (
"errors"
"fmt"
"log/slog"
"os"
"github.com/prometheus/procfs/sysfs"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/procfs"
)
type mdadmCollector struct {
logger *slog.Logger
}
func init() {
registerCollector("mdadm", defaultEnabled, NewMdadmCollector)
}
// NewMdadmCollector returns a new Collector exposing raid statistics.
func NewMdadmCollector(logger *slog.Logger) (Collector, error) {
return &mdadmCollector{logger}, nil
}
var (
activeDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "md", "state"),
"Indicates the state of md-device.",
[]string{"device"},
prometheus.Labels{"state": "active"},
)
inActiveDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "md", "state"),
"Indicates the state of md-device.",
[]string{"device"},
prometheus.Labels{"state": "inactive"},
)
recoveringDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "md", "state"),
"Indicates the state of md-device.",
[]string{"device"},
prometheus.Labels{"state": "recovering"},
)
resyncDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "md", "state"),
"Indicates the state of md-device.",
[]string{"device"},
prometheus.Labels{"state": "resync"},
)
checkDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "md", "state"),
"Indicates the state of md-device.",
[]string{"device"},
prometheus.Labels{"state": "check"},
)
disksDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "md", "disks"),
"Number of active/failed/spare disks of device.",
[]string{"device", "state"},
nil,
)
disksTotalDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "md", "disks_required"),
"Total number of disks of device.",
[]string{"device"},
nil,
)
blocksTotalDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "md", "blocks"),
"Total number of blocks on device.",
[]string{"device"},
nil,
)
blocksSyncedDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "md", "blocks_synced"),
"Number of blocks synced on device.",
[]string{"device"},
nil,
)
mdraidDisks = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "md", "raid_disks"),
"Number of raid disks on device.",
[]string{"device"},
nil,
)
mdraidDegradedDisksDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, "md", "degraded"),
"Number of degraded disks on device.",
[]string{"device"},
nil,
)
)
func (c *mdadmCollector) Update(ch chan<- prometheus.Metric) error {
procFS, err := procfs.NewFS(*procPath)
if err != nil {
return fmt.Errorf("failed to open procfs: %w", err)
}
mdStats, err := procFS.MDStat()
if err != nil {
if errors.Is(err, os.ErrNotExist) {
c.logger.Debug("Not collecting mdstat, file does not exist", "file", *procPath)
return ErrNoData
}
return fmt.Errorf("error parsing mdstatus: %w", err)
}
for _, mdStat := range mdStats {
c.logger.Debug("collecting metrics for device", "device", mdStat.Name)
stateVals := make(map[string]float64)
stateVals[mdStat.ActivityState] = 1
ch <- prometheus.MustNewConstMetric(
disksTotalDesc,
prometheus.GaugeValue,
float64(mdStat.DisksTotal),
mdStat.Name,
)
ch <- prometheus.MustNewConstMetric(
disksDesc,
prometheus.GaugeValue,
float64(mdStat.DisksActive),
mdStat.Name,
"active",
)
ch <- prometheus.MustNewConstMetric(
disksDesc,
prometheus.GaugeValue,
float64(mdStat.DisksFailed),
mdStat.Name,
"failed",
)
ch <- prometheus.MustNewConstMetric(
disksDesc,
prometheus.GaugeValue,
float64(mdStat.DisksSpare),
mdStat.Name,
"spare",
)
ch <- prometheus.MustNewConstMetric(
activeDesc,
prometheus.GaugeValue,
stateVals["active"],
mdStat.Name,
)
ch <- prometheus.MustNewConstMetric(
inActiveDesc,
prometheus.GaugeValue,
stateVals["inactive"],
mdStat.Name,
)
ch <- prometheus.MustNewConstMetric(
recoveringDesc,
prometheus.GaugeValue,
stateVals["recovering"],
mdStat.Name,
)
ch <- prometheus.MustNewConstMetric(
resyncDesc,
prometheus.GaugeValue,
stateVals["resyncing"],
mdStat.Name,
)
ch <- prometheus.MustNewConstMetric(
checkDesc,
prometheus.GaugeValue,
stateVals["checking"],
mdStat.Name,
)
ch <- prometheus.MustNewConstMetric(
blocksTotalDesc,
prometheus.GaugeValue,
float64(mdStat.BlocksTotal),
mdStat.Name,
)
ch <- prometheus.MustNewConstMetric(
blocksSyncedDesc,
prometheus.GaugeValue,
float64(mdStat.BlocksSynced),
mdStat.Name,
)
}
sysFS, err := sysfs.NewFS(*sysPath)
if err != nil {
return fmt.Errorf("failed to open sysfs: %w", err)
}
mdraids, err := sysFS.Mdraids()
if err != nil {
if errors.Is(err, os.ErrNotExist) {
c.logger.Debug("Not collecting mdraids, file does not exist", "file", *sysPath)
return ErrNoData
}
return fmt.Errorf("error parsing mdraids: %w", err)
}
for _, mdraid := range mdraids {
ch <- prometheus.MustNewConstMetric(
mdraidDisks,
prometheus.GaugeValue,
float64(mdraid.Disks),
mdraid.Device,
)
ch <- prometheus.MustNewConstMetric(
mdraidDegradedDisksDesc,
prometheus.GaugeValue,
float64(mdraid.DegradedDisks),
mdraid.Device,
)
}
return nil
}