feat(mac-locations): VLAN-aware port MAC counts, access/trunk thresholds, likely_attachment

Made-with: Cursor
This commit is contained in:
PTah
2026-04-13 13:11:20 +10:00
parent 2d8992549b
commit 42e3c0c925
6 changed files with 244 additions and 16 deletions
+25
View File
@@ -73,6 +73,8 @@ type Store interface {
ListPortDeviceResults(scanID string, filterIP string, filterIfIndex int, limit int) []PortDeviceResult
// ListPortDevicesByMac — строки FDB по точному MAC (mac уже в каноническом виде, см. NormalizeMAC).
ListPortDevicesByMac(scanID string, mac string, limit int) []PortDeviceResult
// CountDistinctMACsPerSwitchPort — число уникальных MAC на каждом (коммутатор, ifIndex, bridge_port, vlan) в скане.
CountDistinctMACsPerSwitchPort(scanID string) (map[string]int, error)
// PurgeAllScanData удаляет все сканы и связанные строки (хосты, порты, SNMP, LLDP, интерфейсы).
PurgeAllScanData() error
}
@@ -482,6 +484,29 @@ func (s *MemoryStore) ListPortDevicesByMac(scanID string, mac string, limit int)
return out
}
func (s *MemoryStore) CountDistinctMACsPerSwitchPort(scanID string) (map[string]int, error) {
s.mu.RLock()
defer s.mu.RUnlock()
items := s.fdb[scanID]
per := make(map[string]map[string]struct{})
for _, r := range items {
mk := NormalizeMAC(r.MAC)
if mk == "" {
continue
}
k := PortFDBLocationKey(r.IP, r.IfIndex, r.BridgePort, r.Vlan)
if per[k] == nil {
per[k] = make(map[string]struct{})
}
per[k][mk] = struct{}{}
}
out := make(map[string]int, len(per))
for k, set := range per {
out[k] = len(set)
}
return out, nil
}
func applyDefaultOptions(opts *ScanOptions) {
if opts.PingTimeoutMS <= 0 {
opts.PingTimeoutMS = 700