Go service (nasmon/) plus nfpm packaging (packaging/), carried over from the home_nas monorepo. Adds rpm packaging alongside the existing deb build (Fedora/RHEL/Rocky dependency names via nfpm overrides, per-format systemd unit path, dpkg+rpm-compatible maintainer scripts) and a Gitea Actions workflow that builds both formats on every push. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
131 lines
4.1 KiB
Go
131 lines
4.1 KiB
Go
package collect
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
const zpoolStatusOK = ` pool: nas_pool_1_8_gb
|
|
state: ONLINE
|
|
scan: scrub repaired 0B in 03:12:41 with 0 errors on Sun Sep 7 03:36:42 2025
|
|
config:
|
|
|
|
NAME STATE READ WRITE CKSUM
|
|
nas_pool_1_8_gb ONLINE 0 0 0
|
|
mirror-0 ONLINE 0 0 0
|
|
ata-WDC_WD80EFZZ-68BTXN0_WD-AAA ONLINE 0 0 0
|
|
ata-WDC_WD80EFZZ-68BTXN0_WD-BBB ONLINE 0 0 0
|
|
|
|
errors: No known data errors
|
|
`
|
|
|
|
const zpoolStatusDegraded = ` pool: tank
|
|
state: DEGRADED
|
|
status: One or more devices could not be opened. Sufficient replicas exist for
|
|
the pool to continue functioning in a degraded state.
|
|
action: Attach the missing device and online it using 'zpool online'.
|
|
see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-2Q
|
|
scan: scrub in progress since Sat Sep 13 01:00:00 2025
|
|
1.20T / 7.10T scanned at 1.02G/s, 500G / 7.10T issued at 430M/s
|
|
0B repaired, 6.88% done, 04:28:12 to go
|
|
config:
|
|
|
|
NAME STATE READ WRITE CKSUM
|
|
tank DEGRADED 0 0 0
|
|
mirror-0 DEGRADED 0 0 0
|
|
ata-DISK_A ONLINE 0 0 3
|
|
1234567890123456789 UNAVAIL 0 0 0 was /dev/sdb1
|
|
logs
|
|
nvme0n1p2 ONLINE 0 0 0
|
|
|
|
errors: Permanent errors have been detected in the following files:
|
|
|
|
/tank/data/file.bin
|
|
`
|
|
|
|
func TestParseZpoolStatusOK(t *testing.T) {
|
|
p := Pool{Name: "nas_pool_1_8_gb"}
|
|
parseZpoolStatus([]byte(zpoolStatusOK), &p)
|
|
if p.Health != "ONLINE" {
|
|
t.Errorf("health = %q", p.Health)
|
|
}
|
|
if p.Errors != "No known data errors" {
|
|
t.Errorf("errors = %q", p.Errors)
|
|
}
|
|
if p.Scrubbing || p.ScanErrs != 0 {
|
|
t.Errorf("scan flags wrong: %+v", p)
|
|
}
|
|
if p.LastScrub == nil {
|
|
t.Fatalf("last scrub not parsed from %q", p.Scan)
|
|
}
|
|
want := time.Date(2025, 9, 7, 3, 36, 42, 0, time.Local)
|
|
if !p.LastScrub.Equal(want) {
|
|
t.Errorf("last scrub = %v, want %v", p.LastScrub, want)
|
|
}
|
|
if len(p.Vdevs) != 4 {
|
|
t.Fatalf("vdevs = %d: %+v", len(p.Vdevs), p.Vdevs)
|
|
}
|
|
if p.Vdevs[0].Depth != 0 || p.Vdevs[1].Depth != 1 || p.Vdevs[2].Depth != 2 {
|
|
t.Errorf("depths wrong: %+v", p.Vdevs)
|
|
}
|
|
if p.Vdevs[1].Name != "mirror-0" || p.Vdevs[1].State != "ONLINE" {
|
|
t.Errorf("mirror vdev wrong: %+v", p.Vdevs[1])
|
|
}
|
|
}
|
|
|
|
func TestParseZpoolStatusDegraded(t *testing.T) {
|
|
p := Pool{Name: "tank"}
|
|
parseZpoolStatus([]byte(zpoolStatusDegraded), &p)
|
|
if p.Health != "DEGRADED" {
|
|
t.Errorf("health = %q", p.Health)
|
|
}
|
|
if !p.Scrubbing {
|
|
t.Errorf("scrub in progress not detected: %q", p.Scan)
|
|
}
|
|
if p.LastScrub != nil {
|
|
t.Errorf("last scrub must be nil while scrubbing")
|
|
}
|
|
if p.Status == "" || p.Action == "" {
|
|
t.Errorf("status/action not captured: %q / %q", p.Status, p.Action)
|
|
}
|
|
if p.Errors == "" || p.Errors == "No known data errors" {
|
|
t.Errorf("errors = %q", p.Errors)
|
|
}
|
|
var unavail, cksum, logs bool
|
|
for _, v := range p.Vdevs {
|
|
switch {
|
|
case v.State == "UNAVAIL" && v.Note == "was /dev/sdb1":
|
|
unavail = true
|
|
case v.Name == "ata-DISK_A" && v.Cksum == 3:
|
|
cksum = true
|
|
case v.Name == "logs" && v.State == "":
|
|
logs = true
|
|
}
|
|
}
|
|
if !unavail || !cksum || !logs {
|
|
t.Errorf("vdev parse: unavail=%v cksum=%v logs=%v %+v", unavail, cksum, logs, p.Vdevs)
|
|
}
|
|
}
|
|
|
|
func TestParseZpoolList(t *testing.T) {
|
|
out := "nas_pool_1_8_gb\t7992048291840\t3500000000000\t4492048291840\t43\tONLINE\t7\ntmp_pool\t1000000000000\t900000000000\t100000000000\t90\tONLINE\t-\n"
|
|
pools := parseZpoolList([]byte(out))
|
|
if len(pools) != 2 {
|
|
t.Fatalf("pools = %d", len(pools))
|
|
}
|
|
if pools[0].CapPct != 43 || pools[0].FragPct != 7 || pools[0].Health != "ONLINE" {
|
|
t.Errorf("pool0 = %+v", pools[0])
|
|
}
|
|
if pools[1].CapPct != 90 || pools[1].FragPct != 0 {
|
|
t.Errorf("pool1 = %+v", pools[1])
|
|
}
|
|
}
|
|
|
|
func TestParseZfsList(t *testing.T) {
|
|
out := "nas_pool_1_8_gb\t100\t200\t50\t/nas_pool_1_8_gb\nnas_pool_1_8_gb/appdata\t10\t200\t10\t/nas_pool_1_8_gb/appdata\n"
|
|
ds := parseZfsList([]byte(out))
|
|
if len(ds) != 2 || ds[1].Name != "nas_pool_1_8_gb/appdata" || ds[1].Avail != 200 {
|
|
t.Errorf("datasets = %+v", ds)
|
|
}
|
|
}
|