pull/3384/merge
Erja Vaskivuori 2 weeks ago committed by GitHub
commit 9fa0df152a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 59
      collector/filesystem_common.go
  2. 53
      collector/filesystem_linux.go
  3. 8
      collector/filesystem_linux_test.go
  4. 5
      collector/paths.go

@ -65,12 +65,26 @@ var (
"Regexp of filesystem types to exclude for filesystem collector. (mutually exclusive to fs-types-exclude)",
).String()
filesystemLabelNames = []string{"device", "mountpoint", "fstype", "device_error"}
fsUUIDsExcludeSet bool
fsUUIDsExclude = kingpin.Flag(
"collector.filesystem.fs-uuids-exclude",
"Regexp of filesystem UUIDs to exclude for filesystem collector. (mutually exclusive to fs-uuids-include)",
).Default("").PreAction(func(c *kingpin.ParseContext) error {
fsUUIDsExcludeSet = true
return nil
}).String()
fsUUIDsInclude = kingpin.Flag(
"collector.filesystem.fs-uuids-include",
"Regexp of filesystem UUIDs to include for filesystem collector. (mutually exclusive to fs-uuids-exclude)",
).String()
filesystemLabelNames = []string{"device", "mountpoint", "fstype", "fsuuid", "device_error"}
)
type filesystemCollector struct {
mountPointFilter deviceFilter
fsTypeFilter deviceFilter
fsUUIDFilter deviceFilter
sizeDesc, freeDesc, availDesc *prometheus.Desc
filesDesc, filesFreeDesc *prometheus.Desc
purgeableDesc *prometheus.Desc
@ -80,7 +94,7 @@ type filesystemCollector struct {
}
type filesystemLabels struct {
device, mountPoint, fsType, mountOptions, superOptions, deviceError, major, minor string
device, mountPoint, fsType, fsUUID, mountOptions, superOptions, deviceError, major, minor string
}
type filesystemStats struct {
@ -164,9 +178,15 @@ func NewFilesystemCollector(logger *slog.Logger) (Collector, error) {
return nil, fmt.Errorf("unable to parse fs types filter flags: %w", err)
}
fsUUIDFilter, err := newFSUUIDFilter(logger)
if err != nil {
return nil, fmt.Errorf("unable to parse fs UUIDs filter flags: %w", err)
}
return &filesystemCollector{
mountPointFilter: mountPointFilter,
fsTypeFilter: fsTypeFilter,
fsUUIDFilter: fsUUIDFilter,
sizeDesc: sizeDesc,
freeDesc: freeDesc,
availDesc: availDesc,
@ -195,11 +215,11 @@ func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) error {
ch <- prometheus.MustNewConstMetric(
c.deviceErrorDesc, prometheus.GaugeValue,
s.deviceError, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.deviceError,
s.deviceError, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.fsUUID, s.labels.deviceError,
)
ch <- prometheus.MustNewConstMetric(
c.roDesc, prometheus.GaugeValue,
s.ro, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.deviceError,
s.ro, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.fsUUID, s.labels.deviceError,
)
if s.deviceError > 0 {
@ -208,23 +228,23 @@ func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) error {
ch <- prometheus.MustNewConstMetric(
c.sizeDesc, prometheus.GaugeValue,
s.size, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.deviceError,
s.size, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.fsUUID, s.labels.deviceError,
)
ch <- prometheus.MustNewConstMetric(
c.freeDesc, prometheus.GaugeValue,
s.free, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.deviceError,
s.free, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.fsUUID, s.labels.deviceError,
)
ch <- prometheus.MustNewConstMetric(
c.availDesc, prometheus.GaugeValue,
s.avail, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.deviceError,
s.avail, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.fsUUID, s.labels.deviceError,
)
ch <- prometheus.MustNewConstMetric(
c.filesDesc, prometheus.GaugeValue,
s.files, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.deviceError,
s.files, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.fsUUID, s.labels.deviceError,
)
ch <- prometheus.MustNewConstMetric(
c.filesFreeDesc, prometheus.GaugeValue,
s.filesFree, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.deviceError,
s.filesFree, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.fsUUID, s.labels.deviceError,
)
ch <- prometheus.MustNewConstMetric(
c.mountInfoDesc, prometheus.GaugeValue,
@ -233,7 +253,7 @@ func (c *filesystemCollector) Update(ch chan<- prometheus.Metric) error {
if s.purgeable >= 0 {
ch <- prometheus.MustNewConstMetric(
c.purgeableDesc, prometheus.GaugeValue,
s.purgeable, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.deviceError,
s.purgeable, s.labels.device, s.labels.mountPoint, s.labels.fsType, s.labels.fsUUID, s.labels.deviceError,
)
}
}
@ -297,3 +317,22 @@ func newFSTypeFilter(logger *slog.Logger) (deviceFilter, error) {
return newDeviceFilter(*fsTypesExclude, *fsTypesInclude), nil
}
func newFSUUIDFilter(logger *slog.Logger) (deviceFilter, error) {
if *fsUUIDsInclude != "" && !fsUUIDsExcludeSet {
logger.Debug("fs-uuids-exclude flag not set when fs-uuids-include flag is set, assuming include is desired")
*fsUUIDsExclude = ""
}
if *fsUUIDsExclude != "" && *fsUUIDsInclude != "" {
return deviceFilter{}, errors.New("--collector.filesystem.fs-uuids-exclude and --collector.filesystem.fs-uuids-include are mutually exclusive")
}
if *fsUUIDsExclude != "" {
logger.Info("Parsed flag --collector.filesystem.fs-uuids-exclude", "flag", *fsUUIDsExclude)
}
if *fsUUIDsInclude != "" {
logger.Info("Parsed flag --collector.filesystem.fs-uuids-include", "flag", *fsUUIDsInclude)
}
return newDeviceFilter(*fsUUIDsExclude, *fsUUIDsInclude), nil
}

@ -19,8 +19,10 @@ import (
"bytes"
"errors"
"fmt"
"io/fs"
"log/slog"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
@ -81,6 +83,11 @@ func (c *filesystemCollector) GetStats() ([]filesystemStats, error) {
continue
}
if c.fsUUIDFilter.ignored(labels.fsUUID) {
c.logger.Debug("Ignoring fs UUID", "uuid", labels.fsUUID)
continue
}
stuckMountsMtx.Lock()
if _, ok := stuckMounts[labels.mountPoint]; ok {
labels.deviceError = "mountpoint timeout"
@ -192,11 +199,47 @@ func mountPointDetails(logger *slog.Logger) ([]filesystemLabels, error) {
if err != nil {
return nil, err
}
UUIDMap := buildUUIDMap(logger)
return parseFilesystemLabels(mountInfo)
return parseFilesystemLabels(mountInfo, UUIDMap)
}
func parseFilesystemLabels(mountInfo []*procfs.MountInfo) ([]filesystemLabels, error) {
// buildUUIDMap builds a map of device major:minor numbers to their filesystem UUIDs.
func buildUUIDMap(logger *slog.Logger) map[string]string {
UUIDMap := map[string]string{}
dir := devDiskFilePath("by-uuid")
_ = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
logger.Debug("Skipping non-file entry", "path", path, "err", err)
return nil
}
uuid := d.Name()
target, err := os.Readlink(path)
if err != nil {
if t, err := filepath.EvalSymlinks(path); err == nil {
target = t
} else {
logger.Debug("Failed to read symlink", "path", path, "err", err)
return nil
}
}
if !filepath.IsAbs(target) {
target = filepath.Join(filepath.Dir(path), target)
}
var stat unix.Stat_t
if err := unix.Stat(target, &stat); err != nil {
logger.Debug("Failed to stat device", "path", path, "err", err)
return nil
}
maj := unix.Major(uint64(stat.Rdev))
min := unix.Minor(uint64(stat.Rdev))
UUIDMap[fmt.Sprintf("%d:%d", maj, min)] = uuid
return nil
})
return UUIDMap
}
func parseFilesystemLabels(mountInfo []*procfs.MountInfo, UUIDMap map[string]string) ([]filesystemLabels, error) {
var filesystems []filesystemLabels
for _, mount := range mountInfo {
@ -206,6 +249,11 @@ func parseFilesystemLabels(mountInfo []*procfs.MountInfo) ([]filesystemLabels, e
return nil, fmt.Errorf("malformed mount point MajorMinorVer: %q", mount.MajorMinorVer)
}
fsUUID, ok := UUIDMap[fmt.Sprintf("%d:%d", major, minor)]
if !ok {
fsUUID = ""
}
// Ensure we handle the translation of \040 and \011
// as per fstab(5).
mount.MountPoint = strings.ReplaceAll(mount.MountPoint, "\\040", " ")
@ -217,6 +265,7 @@ func parseFilesystemLabels(mountInfo []*procfs.MountInfo) ([]filesystemLabels, e
fsType: mount.FSType,
mountOptions: mountOptionsString(mount.Options),
superOptions: mountOptionsString(mount.SuperOptions),
fsUUID: fsUUID,
major: strconv.Itoa(major),
minor: strconv.Itoa(minor),
deviceError: "",

@ -27,8 +27,9 @@ import (
func Test_parseFilesystemLabelsError(t *testing.T) {
tests := []struct {
name string
in []*procfs.MountInfo
name string
in []*procfs.MountInfo
uuids map[string]string
}{
{
name: "malformed Major:Minor",
@ -37,12 +38,13 @@ func Test_parseFilesystemLabelsError(t *testing.T) {
MajorMinorVer: "nope",
},
},
uuids: map[string]string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if _, err := parseFilesystemLabels(tt.in); err == nil {
if _, err := parseFilesystemLabels(tt.in, tt.uuids); err == nil {
t.Fatal("expected an error, but none occurred")
}
})

@ -27,6 +27,7 @@ var (
sysPath = kingpin.Flag("path.sysfs", "sysfs mountpoint.").Default("/sys").String()
rootfsPath = kingpin.Flag("path.rootfs", "rootfs mountpoint.").Default("/").String()
udevDataPath = kingpin.Flag("path.udev.data", "udev data path.").Default("/run/udev/data").String()
devDiskPath = kingpin.Flag("path.dev.disk", "path to /dev/disk").Default("/dev/disk").String()
)
func procFilePath(name string) string {
@ -45,6 +46,10 @@ func udevDataFilePath(name string) string {
return filepath.Join(*udevDataPath, name)
}
func devDiskFilePath(name string) string {
return filepath.Join(*devDiskPath, name)
}
func rootfsStripPrefix(path string) string {
if *rootfsPath == "/" {
return path

Loading…
Cancel
Save