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) и строится граф по кнопке **«Загрузить»** (или автоматически после ожидания).
|
В веб-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
|
## PostgreSQL без Docker
|
||||||
|
|
||||||
### Вариант A: macOS (Homebrew)
|
### Вариант A: macOS (Homebrew)
|
||||||
|
|||||||
@@ -184,6 +184,16 @@ func probeSNMPAndLLDP(ip, community string, checkedAt time.Time, timeoutMS int)
|
|||||||
out.SysObjectID = snmpValueToString(vb.Value)
|
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)
|
return out, probeLLDP(client, ip, checkedAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,16 +249,42 @@ func probeLLDP(client *gosnmp.GoSNMP, ip string, checkedAt time.Time) []LLDPResu
|
|||||||
return out
|
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 {
|
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)
|
pdus, err := client.BulkWalkAll(oid)
|
||||||
if err != nil {
|
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
|
return out
|
||||||
}
|
}
|
||||||
prefix := oid + "."
|
|
||||||
for _, p := range pdus {
|
for _, p := range pdus {
|
||||||
key := strings.TrimPrefix(p.Name, prefix)
|
record(p.Name, p.Value)
|
||||||
out[key] = snmpValueToString(p.Value)
|
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user