The disks box is drives; the net box lets you pick the interface
Disks: one row per physical drive with its model, size, temperature and IO, and a usage bar built from the filesystems the sampler can attribute to it. Attribution is new — a mount's source device is resolved through sysfs to the drive behind it, following one layer of device-mapper or md and folding NVMe namespaces the way the sampler does. A pool member gets no usage bar, because one drive's share of a raidz pool is not a real number. Net: every interface that is up is listed and tappable, defaulting to the one that has actually carried traffic rather than whichever the kernel listed first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
bf0bdd54af
commit
e0625be684
@@ -94,6 +94,11 @@ type FS struct {
|
||||
// sshfs, and so on. Its bytes are somebody else's disk, so a panel about this machine's
|
||||
// hardware leaves it out, while the sensor stays for anyone who does want it.
|
||||
Remote bool `json:"remote,omitempty"`
|
||||
// Disk is the physical drive these bytes are on ("nvme0", "sda"), named as the sampler
|
||||
// names it; empty when that cannot be
|
||||
// said honestly: a ZFS pool or an md array spreads a filesystem over several drives, and
|
||||
// a remote mount is on none of them.
|
||||
Disk string `json:"disk,omitempty"`
|
||||
}
|
||||
|
||||
type UPS struct {
|
||||
@@ -825,7 +830,7 @@ func filesystems() []FS {
|
||||
return
|
||||
}
|
||||
bs := uint64(st.Bsize)
|
||||
done <- FS{Mount: mount, Type: fs[2], Size: st.Blocks * bs, Used: (st.Blocks - st.Bfree) * bs, Remote: remoteFS[fs[2]]}
|
||||
done <- FS{Mount: mount, Type: fs[2], Size: st.Blocks * bs, Used: (st.Blocks - st.Bfree) * bs, Remote: remoteFS[fs[2]], Disk: diskOf(fs[0])}
|
||||
}()
|
||||
select {
|
||||
case r := <-done:
|
||||
@@ -839,6 +844,46 @@ func filesystems() []FS {
|
||||
return out
|
||||
}
|
||||
|
||||
// diskOf names the physical drive a mount's source device sits on — "/dev/nvme0n1p2" is on
|
||||
// "nvme0n1". It follows one layer of device-mapper or md (LUKS on a partition, say) but
|
||||
// gives up when a thing spans several drives, because then no single drive is the answer.
|
||||
func diskOf(source string) string {
|
||||
if !strings.HasPrefix(source, "/dev/") {
|
||||
return "" // a ZFS pool, a network share, a tmpfs: not a block device at all
|
||||
}
|
||||
d := parentDisk(strings.TrimPrefix(source, "/dev/"), 0)
|
||||
// the sampler reports one drive per NVMe controller, so name it the same way
|
||||
if m := nsRe.FindStringSubmatch(d); m != nil {
|
||||
return m[1]
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func parentDisk(name string, depth int) string {
|
||||
if name == "" || depth > 4 || strings.Contains(name, "/") {
|
||||
return ""
|
||||
}
|
||||
base := "/sys/class/block/" + name
|
||||
if _, err := os.Stat(base); err != nil {
|
||||
return ""
|
||||
}
|
||||
// a partition: its parent directory in sysfs is the whole drive
|
||||
if _, err := os.Stat(base + "/partition"); err == nil {
|
||||
if target, err := os.Readlink(base); err == nil {
|
||||
return filepath.Base(filepath.Dir(target))
|
||||
}
|
||||
return ""
|
||||
}
|
||||
// device-mapper or md: one drive underneath is an answer, several is not
|
||||
if ents, err := os.ReadDir(base + "/slaves"); err == nil && len(ents) > 0 {
|
||||
if len(ents) > 1 {
|
||||
return ""
|
||||
}
|
||||
return parentDisk(ents[0].Name(), depth+1)
|
||||
}
|
||||
return name // a whole drive, used without partitioning
|
||||
}
|
||||
|
||||
// ---- UPS ----
|
||||
|
||||
func runOut(args ...string) (string, bool) {
|
||||
|
||||
@@ -123,6 +123,9 @@ func flatten(s Sample, want func(id string) bool) []sensors.Sensor {
|
||||
if x.Remote {
|
||||
lbl = append(lbl, "remote", "1")
|
||||
}
|
||||
if x.Disk != "" {
|
||||
lbl = append(lbl, "disk", x.Disk)
|
||||
}
|
||||
f.add(p+".size", x.Mount+" size", "bytes", "B", float64(x.Size), s.fsT, none, lbl...)
|
||||
f.add(p+".used", x.Mount+" used", "bytes", "B", float64(x.Used), s.fsT, upTo(float64(x.Size)), lbl...)
|
||||
f.add(p+".used_pct", x.Mount+" used", "usage", "%", ratio(x.Used, x.Size), s.fsT, pct, lbl...)
|
||||
|
||||
Reference in New Issue
Block a user