aa4dfb75ea
Add a host-wide, token-authed GET /host/metrics local-API endpoint that re-serves the slice-4 collector's host + per-storage view to the customer (the de-privileged controller can't read the host itself). Add the one new collector — CPU/chassis temperature via sysfs hwmon/thermal-zones, graceful- null — to the shared HostMetrics struct, so the hub report carries cpu_temp_c too. Cross-repo host-report golden updated byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
50 lines
2.2 KiB
Go
50 lines
2.2 KiB
Go
package localapi
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-agent/internal/hub"
|
|
)
|
|
|
|
// Host metrics (slice 9, doc 03 §6). The de-privileged controller (slice 8C) can only see its own
|
|
// cgroup, so it cannot read host health itself. This endpoint re-serves the slice-4 collector's
|
|
// host + per-storage view to the customer so the controller can render the box's health.
|
|
//
|
|
// Host-wide, token-authed, fresh: the metrics are about the BOX (not per-guest), so any valid
|
|
// per-guest token gets the host-wide view (assumption: one customer per host — the home-server
|
|
// model). It is a live collect (fresh cpu%/temp), not the 15-min hub-report snapshot.
|
|
|
|
// HostMetricsResponse is GET /host/metrics: the host block + per-storage capacity targets.
|
|
type HostMetricsResponse struct {
|
|
VMID int `json:"vmid"`
|
|
Host hub.HostMetrics `json:"host"` // cpu%/mem/load/uptime/cpu_temp_c
|
|
StorageTargets []hub.StorageTarget `json:"storage_targets"` // per-storage total/used/thin-pool/SMART temp+wear
|
|
}
|
|
|
|
// handleHostMetrics serves a fresh host-health snapshot. The host block comes from a live
|
|
// collector read; the per-storage capacity comes from the observer (the same source the hub
|
|
// report uses). Best-effort on storage: a storage-view error still returns the host block (the
|
|
// CPU/mem/temp view is the headline) with an empty targets list.
|
|
func (s *Server) handleHostMetrics(w http.ResponseWriter, r *http.Request, vmid int) {
|
|
if s.hostMetrics == nil {
|
|
writeErr(w, http.StatusServiceUnavailable, "host metrics not configured on this host")
|
|
return
|
|
}
|
|
host, err := s.hostMetrics.HostMetricsNow(r.Context())
|
|
if err != nil {
|
|
s.logger.Error("local-api: /host/metrics collect", "vmid", vmid, "err", err)
|
|
writeErr(w, http.StatusBadGateway, "could not read host metrics")
|
|
return
|
|
}
|
|
targets, err := s.storage.Observe(r.Context())
|
|
if err != nil {
|
|
// Host health is the headline — don't sink it on a storage-view hiccup.
|
|
s.logger.Warn("local-api: /host/metrics storage view unavailable", "vmid", vmid, "err", err)
|
|
targets = []hub.StorageTarget{}
|
|
}
|
|
if targets == nil {
|
|
targets = []hub.StorageTarget{}
|
|
}
|
|
writeOK(w, HostMetricsResponse{VMID: vmid, Host: host, StorageTargets: targets})
|
|
}
|