Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions server-go/internal/adapters/device_seen.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,28 @@ func saneIP(ip string) string {
return ip
}

// filterTombstoned (#1292): quita de la LISTA FINAL las MACs enterradas.
// Una fuente viva (una estación stale del AP) puede seguir emitiéndolas en
// cada ciclo sin que pasen por device_seen (ni first_seen ni last_seen):
// el filtro del registro no basta, la lista misma se filtra.
func (l *Live) filterTombstoned(devices []Device) []Device {
if l.db == nil || len(devices) == 0 {
return devices
}
tomb, err := l.db.DeletedClients()
if err != nil || len(tomb) == 0 {
return devices
}
out := devices[:0]
for _, d := range devices {
if tomb[normSeenMAC(d.MAC)] {
continue
}
out = append(out, d)
}
return out
}

// purgeFleetSeen (#1278): borra del registro la MAC base de las unidades de
// flota. Las bocas/gestiones de los propios equipos se aprendían como
// "clientes" vía FDB de los switches y quedaban como fantasmas offline para
Expand Down
17 changes: 17 additions & 0 deletions server-go/internal/adapters/device_seen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,3 +161,20 @@ func TestDeletedClientTombstone(t *testing.T) {
t.Errorf("tras quitar la lápida la MAC se da de alta (%d filas)", n)
}
}

// #1292: la lápida filtra la LISTA FINAL - una estación stale del AP puede
// re-emitir la MAC sin que pase por device_seen.
func TestFilterTombstonedFinalList(t *testing.T) {
d := openLiveTestDB(t)
l := &Live{db: d}
if err := d.AddDeletedClient("12:77:BC:5A:5A:FA"); err != nil {
t.Fatal(err)
}
devs := l.filterTombstoned([]Device{
{MAC: "12:77:BC:5A:5A:FA", Online: true},
{MAC: "AA:BB:CC:DD:EE:FF", Online: true},
})
if len(devs) != 1 || devs[0].MAC != "AA:BB:CC:DD:EE:FF" {
t.Errorf("filterTombstoned = %+v", devs)
}
}
6 changes: 6 additions & 0 deletions server-go/internal/adapters/live.go
Original file line number Diff line number Diff line change
Expand Up @@ -3178,6 +3178,9 @@ func (l *Live) buildOverview(ctx context.Context) (*Overview, error) {
l.mu.Unlock()
}
}
// #1292: lápidas filtradas de la lista final (una estación stale del AP
// re-emite la MAC en cada ciclo sin pasar por device_seen).
devices = l.filterTombstoned(devices)
l.trackUnknownDevices(devices, distNodes)
l.trackDevicePresence(devices, time.Now().UnixMilli())
// #954: first/last seen persistente de TODOS los clientes online.
Expand Down Expand Up @@ -3707,6 +3710,9 @@ func (l *Live) attributedDevices() []Device {
overlayStickyFdb(polled, detailMemo, detailNowMs),
l.buildDevices(polled),
)
// #1292: lápidas fuera de la lista del detalle (la fuente viva puede
// seguir emitiendo la MAC borrada).
devices = l.filterTombstoned(devices)
// #561: sellado de infraestructura con el inventario PVE (si configurado).
// El detalle no consume distnodes, pero el sellado de devices sí corre.
l.sealProxmoxInfra(devices, nil)
Expand Down
Loading