v0.184.0 — E-2b + Part 5: wire the drive-absent alarm that was never called

NotifyStorageDisconnected and NotifyStorageReconnected were defined and called
from NOWHERE. Registered in allowedEventTypes, in DefaultEnabledEvents, and given
a Hungarian message on the hub -- and never invoked. A drive going absent produced
apps stopped, a WARN log and a UI badge, then silence on every channel. Verified
against the gitignored-cmd/ trap with a positive control. Fifth instance of this
class, found by E-2 Phase 0 rather than by a failure.

A drive that is ONLY a backup target has no apps to stop, so it was silent twice.

ReconcileDriveGates now calls both halves. When the absent drive is the whole-guest
backup target it raises the more specific backup_target_absent (error) instead --
never both, since two mails for one event trains people to ignore the channel --
and recovers as backup_target_restored (info, the existing pairing-gated pattern;
severityNotifies NOT widened). The recovery mirrors the alarm's choice or the
operator cannot match them.

Which drive is the target comes from the AGENT (/disks backup_target, >= 0.112.0),
not from our StoragePath.BackupTarget: that is customer INTENT, and on the two
boxes migrated by hand in E-1 the intent was never recorded while the drive really
is the target. An older agent omits the field -> false -> generic alarm, never a
wrong one.

Before this an absent backup target had NO prompt signal: the tier stays DUE
(targetStoragePresent checks name presence, never reachability), so the only
evidence was its own failure at the next due cycle, up to ~24h away. The R-100
shape.

Tests observe the WIRE, not a mock, because the failure class is "nothing
arrives": a real Notifier posts to an httptest hub and the test asserts the event
type and severity that actually went out.

MinAgent: 0.112.0
Green gate: build + vet + test rc=0 (27 packages), run separately from this commit.
This commit is contained in:
2026-07-29 08:21:25 +02:00
parent ff058a4f10
commit c1a63de1c7
5 changed files with 228 additions and 0 deletions
+6
View File
@@ -323,6 +323,12 @@ type DiskInfo struct {
// opposed to merely present on the host (F9) — the signal whose absence let the HDD look available
// when it wasn't attached. LEGACY (per-drive mp model); the intermediary model uses BoundUnderParent.
GuestAttached bool `json:"guest_attached"`
// BackupTarget (E-2, agent >= v0.112.0) reports that this drive backs the PRIMARY whole-guest
// backup tier. The agent is the only component that can answer: our own
// settings.StoragePath.BackupTarget is customer INTENT, and on a box migrated by hand (E-1) that
// intent was never recorded while the drive really IS the target. Absent on an older agent →
// false, which degrades to the pre-E-2 behaviour (a generic disconnect alarm, never a wrong one).
BackupTarget bool `json:"backup_target,omitempty"`
// GuestPath is the drive's STABLE in-guest path in the intermediary-mount model
// (/mnt/felhom-drives/<name>) — what the controller registers + repoints HDD_PATH to. Distinct from
// MountPath (the raw /mnt/<name> host PVE mount the agent ops on). "" for non-user-data drives.