hub v0.23.0: host root-disk pressure monitoring + alert
New HostDiskChecker on the 60s sweep alerts the operator when a Proxmox host root filesystem crosses warn (90%) / crit (95%). Born/persistent (a disk already full at hub restart alerts on cycle 1); distinct host_disk_* event types from the guest disk_*; critical band maps to severity error (the dispatcher only routes warning/error). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HxLA1mZurFq9kt8hneFeCs
This commit is contained in:
@@ -1683,6 +1683,54 @@ type HostLeafRow struct {
|
||||
LeafFP string
|
||||
}
|
||||
|
||||
// HostDiskRow is the per-host root-filesystem usage the HostDiskChecker reads: the denormalized
|
||||
// disk_percent column (the threshold signal) plus total/used bytes parsed from report_json (event detail
|
||||
// only). DiskPercent is the HOST root fs (agent HostMetrics) — distinct from the controller's GUEST cgroup
|
||||
// disk. A NULL/absent disk_percent (a pre-disk-reporting agent) yields 0 → the checker treats it as ok.
|
||||
type HostDiskRow struct {
|
||||
HostID string
|
||||
CustomerID string
|
||||
DiskPercent float64
|
||||
DiskTotalBytes int64
|
||||
DiskUsedBytes int64
|
||||
}
|
||||
|
||||
// GetHostDiskUsage returns the latest root-fs usage per host (MAX(id) per host, mirroring
|
||||
// GetHostCapabilities / GetHostLeafFingerprints). disk_percent comes from the denorm column; total/used
|
||||
// bytes are parsed from the report body's host block for the event detail (no schema migration needed).
|
||||
func (s *Store) GetHostDiskUsage() ([]HostDiskRow, error) {
|
||||
rows, err := s.db.Query(`
|
||||
SELECT hr.host_id, hr.customer_id, hr.disk_percent, hr.report_json
|
||||
FROM host_reports hr
|
||||
JOIN (SELECT host_id, MAX(id) AS mx FROM host_reports GROUP BY host_id) latest
|
||||
ON hr.id = latest.mx`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []HostDiskRow
|
||||
for rows.Next() {
|
||||
var r HostDiskRow
|
||||
var dp sql.NullFloat64
|
||||
var reportJSON string
|
||||
if err := rows.Scan(&r.HostID, &r.CustomerID, &dp, &reportJSON); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.DiskPercent = dp.Float64
|
||||
var body struct {
|
||||
Host struct {
|
||||
DiskTotalBytes int64 `json:"disk_total_bytes"`
|
||||
DiskUsedBytes int64 `json:"disk_used_bytes"`
|
||||
} `json:"host"`
|
||||
}
|
||||
_ = json.Unmarshal([]byte(reportJSON), &body) // malformed/old body → zero bytes (detail only)
|
||||
r.DiskTotalBytes = body.Host.DiskTotalBytes
|
||||
r.DiskUsedBytes = body.Host.DiskUsedBytes
|
||||
out = append(out, r)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// GetHostLeafFingerprints returns the latest reported local-API leaf fp per host (mirrors
|
||||
// GetHostCapabilities — MAX(id) per host, parsed from report_json so there is no schema migration).
|
||||
func (s *Store) GetHostLeafFingerprints() ([]HostLeafRow, error) {
|
||||
|
||||
Reference in New Issue
Block a user