Files
pulse/pkg/diskinventory
rcourtman a85bdcdc9a Stop a stale agent SMART row lending a replaced disk its identity (#2658)
## Problem

PR #2648 stopped the poller's previous-evidence lookup from handing a
replaced disk the old disk's serial. The linked host agent's SMART merge
runs before that lookup and had the same flaw. Its last-resort match is
the device path, and a path names a slot, not a disk. When a disk is
swapped before the agent reports again, or after the agent went silent
and kept its last rows, the agent's row for the old disk matched the
replacement. It filled the old serial or, for SAS, promoted it over the
Proxmox SAS address. The registry then keyed the replacement to the old
disk's canonical resource and SMART history, and it also took the old
disk's temperature and FAILED health. For SAS, the registry's same-path
join (`resolveLinkedPhysicalDisk`) did the same at the canonical level
even when monitoring kept the rows apart.

Proxmox's disk inventory (`PVE::Diskmanage`, checked on a PVE 9 node)
writes the literal `unknown` for a serial or WWN udev cannot read. The
poller recorded it as a collected serial, so the collection state hid
the gap, and the merge's `Serial == ""` fill never let the agent's real
serial replace it.

## Change

- `mergeHostAgentSMARTIntoDisks` refuses a device-path row whose
identity contradicts the Proxmox disk's. The new
`diskinventory.HardwareIdentityConflict` defines a contradiction:
nothing agrees across serial and WWN, and a field both producers report
like for like disagrees.
  - WWNs count for every disk.
- Serials count only where Proxmox's serial is the drive's own, as
smartctl's is: a disk typed `nvme`, or one presented with SCSI vendor
`ATA` (libata, or a SAS HBA's SCSI-ATA translation) and not typed
`usb`/`sas`. Elsewhere the Proxmox serial may be a SAS address or SCSI
designator (#1595) or a USB bridge's own serial, so it only counts
toward agreement.
- Spelling differences are not disagreement: udev's underscores for
whitespace, a T10 designator for an ATA drive (`ATA`, the model, then
the serial), the agent's NAA field spelling, and udev's 64-bit prefix of
an NAA 6 WWN.
- The refusal runs after the unique-path check, so a second row on the
path keeps the match ambiguous.
- The merge's WWN step compares normalized identity
(`HardwareIdentityMatch`), so the agent's `5-c50-a1b2c3d4` finds the
Proxmox `0x5000c500a1b2c3d4` disk.
- `resolveLinkedPhysicalDisk` refuses the SAS same-path fallback when
the two WWNs name different disks.
- The poller records Proxmox's `unknown` serial/WWN as empty, so the
serial reads `missing` and the agent can fill it. Retained evidence
carries a previous serial only when it is usable, so a pre-upgrade
record cannot restore the placeholder. Identity keys already ignored the
placeholder. When the agent now fills a disk's serial, that disk's
Proxmox-poll SMART samples move from its WWN or path key to the serial
key, where the agent's own samples already go.

## Tests

- `TestHostAgentSMARTRowForSwappedOutDiskDoesNotLendItsIdentity` (sata
and sas) drives the real poller and registry. Proxmox reports `unknown`,
the agent fills the serial, the agent goes silent, and the disk is
swapped. On main, the SATA replacement takes the old serial and
resource; the SAS replacement keeps its own serial in state, but the
registry joins it to the old resource with its temperature and FAILED
health.
- `TestHostAgentSMARTMergeRefusesPathMatchAcrossContradictingIdentity`
has one row per producer rule: SAS/SATA/NVMe replacements refused;
same-disk SAS without an agent WWN, a renamed SAS disk in the agent's
WWN spelling, udev's serial spelling, a USB enclosure (typed `usb` and
typed `ssd`), a T10 designator and an NAA 6 volume merged; and an
ambiguous path.
- `TestRegistrySASPathJoinRefusesContradictingWWN`,
`TestProxmoxUnknownDiskIdentityIsRecordedAsUnreported` and
`TestHardwareIdentityConflict` cover the rest.
- Negative controls via `go test -overlay`: removing each rule fails
only its own case. These cover the guard, the WWN normalization, the
transport/vendor rule, compaction, the designator rule, the NAA 6
prefix, the registry guard, the ingest normalization and the
usable-serial carry. Against main's `monitor.go` and `registry.go`, the
end-to-end, merge and registry tests fail.
- `TestMergeHostAgentSMARTIntoDisks_PreservesExistingProxmoxIdentity`
merged a row with a different WWN by path alone, which the guard now
rightly refuses. Its row is now the same disk in the agent's WWN
spelling, with all assertions unchanged.
- `internal/monitoring`, `internal/unifiedresources` and
`pkg/diskinventory` pass in full.

## Not covered

- A replacement with no comparable identity still matches its path: no
usable serial or WWN at all, or a SAS/SCSI/USB disk with a WWN on one
side only.
- The registry's seeded source mapping, reused after a restart by the
slot-shaped Proxmox source ID, can still hand a swapped disk the old
canonical resource. That's a separate follow-up.
2026-10-07 14:01:19 +01:00
..