Initial import: nasmon health monitor, split out of home_nas
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>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
// Package collect reads system state from /proc, /sys and CLI tools.
|
||||
package collect
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CPU struct {
|
||||
UsagePct float64 `json:"usage_pct"`
|
||||
Cores int `json:"cores"`
|
||||
Load [3]float64 `json:"load"`
|
||||
}
|
||||
|
||||
type cpuTimes struct{ idle, total uint64 }
|
||||
|
||||
// CPUSampler keeps the previous /proc/stat reading to compute usage deltas.
|
||||
type CPUSampler struct {
|
||||
prev cpuTimes
|
||||
init bool
|
||||
}
|
||||
|
||||
func (s *CPUSampler) Sample() (CPU, error) {
|
||||
b, err := os.ReadFile("/proc/stat")
|
||||
if err != nil {
|
||||
return CPU{}, err
|
||||
}
|
||||
cur, err := parseProcStat(b)
|
||||
if err != nil {
|
||||
return CPU{}, err
|
||||
}
|
||||
c := CPU{Cores: runtime.NumCPU()}
|
||||
if s.init && cur.total > s.prev.total {
|
||||
dt := float64(cur.total - s.prev.total)
|
||||
di := float64(cur.idle - s.prev.idle)
|
||||
c.UsagePct = 100 * (1 - di/dt)
|
||||
}
|
||||
s.prev, s.init = cur, true
|
||||
if lb, err := os.ReadFile("/proc/loadavg"); err == nil {
|
||||
f := strings.Fields(string(lb))
|
||||
for i := 0; i < 3 && i < len(f); i++ {
|
||||
c.Load[i], _ = strconv.ParseFloat(f[i], 64)
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// parseProcStat returns idle and total jiffies from the aggregate "cpu" line.
|
||||
func parseProcStat(b []byte) (cpuTimes, error) {
|
||||
sc := bufio.NewScanner(bytes.NewReader(b))
|
||||
for sc.Scan() {
|
||||
f := strings.Fields(sc.Text())
|
||||
if len(f) < 5 || f[0] != "cpu" {
|
||||
continue
|
||||
}
|
||||
var t cpuTimes
|
||||
for i, v := range f[1:] {
|
||||
n, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
t.total += n
|
||||
if i == 3 || i == 4 { // idle + iowait
|
||||
t.idle += n
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
return cpuTimes{}, fmt.Errorf("no cpu line in /proc/stat")
|
||||
}
|
||||
Reference in New Issue
Block a user