node_exporter/collector/filesystem_common.go
Brandon Gilmore 76bbd8dd18 Use /proc/mounts instead of statfs(2) for ro state (#1002)
While the statfs(2) approach is reliable for normally mounted filesystems, the
flags returned can be inconsistent when filesystem has been remounted read-only
after encountering an error. The returned flags do accurately represent the
internal state of the filesystem, but they do not reflect whether the VFS layer
will accept writes. Instead, it makes sense to parse the current VFS mount
state from the options field in /proc/mounts since it takes precedence.

Signed-off-by: Brandon Gilmore <bgilmore@valvesoftware.com>
2018-07-16 15:56:27 +02:00

177 lines
5.2 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.
// +build !nofilesystem
// +build linux freebsd openbsd darwin,amd64 dragonfly
package collector
import (
"regexp"
"github.com/prometheus/client_golang/prometheus"
"gopkg.in/alecthomas/kingpin.v2"
)
// Arch-dependent implementation must define:
// * defIgnoredMountPoints
// * defIgnoredFSTypes
// * filesystemLabelNames
// * filesystemCollector.GetStats
var (
ignoredMountPoints = kingpin.Flag(
"collector.filesystem.ignored-mount-points",
"Regexp of mount points to ignore for filesystem collector.",
).Default(defIgnoredMountPoints).String()
ignoredFSTypes = kingpin.Flag(
"collector.filesystem.ignored-fs-types",
"Regexp of filesystem types to ignore for filesystem collector.",
).Default(defIgnoredFSTypes).String()
filesystemLabelNames = []string{"device", "mountpoint", "fstype"}
)
type filesystemCollector struct {
ignoredMountPointsPattern *regexp.Regexp
ignoredFSTypesPattern *regexp.Regexp
sizeDesc, freeDesc, availDesc *prometheus.Desc
filesDesc, filesFreeDesc *prometheus.Desc
roDesc, deviceErrorDesc *prometheus.Desc
}
type filesystemLabels struct {
device, mountPoint, fsType, options string
}
type filesystemStats struct {
labels filesystemLabels
size, free, avail float64
files, filesFree float64
ro, deviceError float64
}
func init() {
registerCollector("filesystem", defaultEnabled, NewFilesystemCollector)
}
// NewFilesystemCollector returns a new Collector exposing filesystems stats.
func NewFilesystemCollector() (Collector, error) {
subsystem := "filesystem"
mountPointPattern := regexp.MustCompile(*ignoredMountPoints)
filesystemsTypesPattern := regexp.MustCompile(*ignoredFSTypes)
sizeDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "size_bytes"),
"Filesystem size in bytes.",
filesystemLabelNames, nil,
)
freeDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "free_bytes"),
"Filesystem free space in bytes.",
filesystemLabelNames, nil,
)
availDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "avail_bytes"),
"Filesystem space available to non-root users in bytes.",
filesystemLabelNames, nil,
)
filesDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "files"),
"Filesystem total file nodes.",
filesystemLabelNames, nil,
)
filesFreeDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "files_free"),
"Filesystem total free file nodes.",
filesystemLabelNames, nil,
)
roDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "readonly"),
"Filesystem read-only status.",
filesystemLabelNames, nil,
)
deviceErrorDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "device_error"),
"Whether an error occurred while getting statistics for the given device.",
filesystemLabelNames, nil,
)
return &filesystemCollector{
ignoredMountPointsPattern: mountPointPattern,
ignoredFSTypesPattern: filesystemsTypesPattern,
sizeDesc: sizeDesc,
freeDesc: freeDesc,
availDesc: availDesc,
filesDesc: filesDesc,
filesFreeDesc: filesFreeDesc,
roDesc: roDesc,
deviceErrorDesc: deviceErrorDesc,
}, nil
}
func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) error {
stats, err := c.GetStats()
if err != nil {
return err
}
// Make sure we expose a metric once, even if there are multiple mounts
seen := map[filesystemLabels]bool{}
for _, s := range stats {
if seen[s.labels] {
continue
}
seen[s.labels] = true
ch <- prometheus.MustNewConstMetric(
c.deviceErrorDesc, prometheus.GaugeValue,
s.deviceError, s.labels.device, s.labels.mountPoint, s.labels.fsType,
)
if s.deviceError > 0 {
continue
}
ch <- prometheus.MustNewConstMetric(
c.sizeDesc, prometheus.GaugeValue,
s.size, s.labels.device, s.labels.mountPoint, s.labels.fsType,
)
ch <- prometheus.MustNewConstMetric(
c.freeDesc, prometheus.GaugeValue,
s.free, s.labels.device, s.labels.mountPoint, s.labels.fsType,
)
ch <- prometheus.MustNewConstMetric(
c.availDesc, prometheus.GaugeValue,
s.avail, s.labels.device, s.labels.mountPoint, s.labels.fsType,
)
ch <- prometheus.MustNewConstMetric(
c.filesDesc, prometheus.GaugeValue,
s.files, s.labels.device, s.labels.mountPoint, s.labels.fsType,
)
ch <- prometheus.MustNewConstMetric(
c.filesFreeDesc, prometheus.GaugeValue,
s.filesFree, s.labels.device, s.labels.mountPoint, s.labels.fsType,
)
ch <- prometheus.MustNewConstMetric(
c.roDesc, prometheus.GaugeValue,
s.ro, s.labels.device, s.labels.mountPoint, s.labels.fsType,
)
}
return nil
}