SNMP LLDP: longer timeout, lower MaxRepetitions, Walk fallback after BulkWalk errors (e.g. Ubiquiti EdgeSwitch).
Made-with: Cursor
This commit is contained in:
@@ -94,6 +94,10 @@ SNMP-проверка по умолчанию включена (`SNMP_ENABLED=tr
|
||||
|
||||
В веб-UI после **«Создать scan и ждать»** опрашивается статус, показывается расшифровка в строке состояния, по завершении обновляется таблица **найденных устройств** (ping + SNMP) и строится граф по кнопке **«Загрузить»** (или автоматически после ожидания).
|
||||
|
||||
### LLDP пустой на коммутаторе (например Ubiquiti EdgeSwitch)
|
||||
|
||||
Опрос LLDP идёт по стандартному **LLDP-MIB** (`1.0.8802.1.1.2.1.4.1.1.*`) после успешного SNMP **Get** системной группы. Раньше при **GET-BULK** часть прошивок отвечала слишком долго или с ошибкой — обход обрывался, в БД не попадало ни строки («No LLDP data» в UI). Сейчас для фазы LLDP увеличен таймаут (не меньше **3 c** на запрос), снижен размер «пачки» **MaxRepetitions**, добавлен **повтор при ошибке через SNMP Walk (GetNext)**. В логах сервера ищите строки `lldp snmp BulkWalkAll` и `fallback Walk`. Проверьте с хоста nettopo: community (**`SNMP_COMMUNITY` / `public`**), доступ UDP **161** с этого сервера и что на свиче включён **SNMP v2c** (не только LLDP в L2, но и ответ агента SNMP).
|
||||
|
||||
## PostgreSQL без Docker
|
||||
|
||||
### Вариант A: macOS (Homebrew)
|
||||
|
||||
@@ -184,6 +184,16 @@ func probeSNMPAndLLDP(ip, community string, checkedAt time.Time, timeoutMS int)
|
||||
out.SysObjectID = snmpValueToString(vb.Value)
|
||||
}
|
||||
}
|
||||
|
||||
// LLDP — несколько BulkWalk подряд; часть прошивок (в т.ч. Ubiquiti EdgeSwitch) отваливается по таймауту
|
||||
// или некорректно обрабатывает GET-BULK с большим MaxRepetitions — увеличиваем время и снижаем пакет,
|
||||
// при ошибке BulkWalk в walkAsMap делаем запасной SNMP Walk (GetNext).
|
||||
if min := time.Duration(3000) * time.Millisecond; client.Timeout < min {
|
||||
client.Timeout = min
|
||||
}
|
||||
client.MaxRepetitions = 12
|
||||
client.Retries = 2
|
||||
|
||||
return out, probeLLDP(client, ip, checkedAt)
|
||||
}
|
||||
|
||||
@@ -239,16 +249,42 @@ func probeLLDP(client *gosnmp.GoSNMP, ip string, checkedAt time.Time) []LLDPResu
|
||||
return out
|
||||
}
|
||||
|
||||
func snmpOIDTrim(s string) string {
|
||||
return strings.TrimPrefix(strings.TrimSpace(s), ".")
|
||||
}
|
||||
|
||||
// walkAsMap выполняет поддерево SNMP по base oid. Сначала GET-BULK (быстро); при ошибке — Walk (GetNext),
|
||||
// что совместимо с большим числом встраиваемых коммутаторов.
|
||||
func walkAsMap(client *gosnmp.GoSNMP, oid string) map[string]string {
|
||||
out := map[string]string{}
|
||||
base := snmpOIDTrim(oid)
|
||||
prefix := base + "."
|
||||
out := make(map[string]string)
|
||||
|
||||
record := func(pduName string, val any) {
|
||||
n := snmpOIDTrim(pduName)
|
||||
if !strings.HasPrefix(n, prefix) {
|
||||
return
|
||||
}
|
||||
key := strings.TrimPrefix(n, prefix)
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
out[key] = snmpValueToString(val)
|
||||
}
|
||||
|
||||
pdus, err := client.BulkWalkAll(oid)
|
||||
if err != nil {
|
||||
log.Printf("lldp snmp BulkWalkAll target=%s oid=%s: %v (fallback Walk)", client.Target, base, err)
|
||||
if werr := client.Walk(oid, func(pdu gosnmp.SnmpPDU) error {
|
||||
record(pdu.Name, pdu.Value)
|
||||
return nil
|
||||
}); werr != nil {
|
||||
log.Printf("lldp snmp Walk target=%s oid=%s: %v", client.Target, base, werr)
|
||||
}
|
||||
return out
|
||||
}
|
||||
prefix := oid + "."
|
||||
for _, p := range pdus {
|
||||
key := strings.TrimPrefix(p.Name, prefix)
|
||||
out[key] = snmpValueToString(p.Value)
|
||||
record(p.Name, p.Value)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user