CreaRack-SL

Hotfix 2.21.5: un walk SNMP vencido por timeout fabricaba un 0 (Regla 13) — 47 APs con dato falso 30 días

Cuándo

25-08-2026, un día después de la racha de 4 hotfixes del bucle SNMP del 24-08 (2.21.1 → 2.21.4, ver incidentes hermanos). Edu midió el problema desde su portátil, sin ruta VPN al CCIB, comparando contra un Xirrus de oficina y contra hosts sin ruta.

Síntomas visibles

Dos síntomas relacionados, con la misma causa de fondo (un walk SNMP que vence por timeout):

  • Dato fabricado: 47 puntos de acceso llevaban 30 días produciendo snmp_fast_station_count=0, que parecía una lectura real (tabla vacía = sin clientes conectados). Ese 0 falso además reiniciaba el contador de cuelgues del motor, como si el equipo hubiera respondido con normalidad — ocultando el problema en dos sitios a la vez: el valor mostrado y la señal de salud.
  • Métricas extra a cero: las métricas “extras” del CCIB llevaban en cero desde el hotfix 2.21.2. Un AP mudo costaba 4 lotes de GET (5 s cada uno) más el walk — 27 s reales medidos — y el tope de 7 s del bucle (SNMP_HARD_TIMEOUT, pensado para una sola petición) cortaba el sondeo ENTERO como si fuera un cuelgue, antes de completar ni un host.

Causa raíz

pysnmp señala que un equipo no contestó como un errorIndication de timeout dentro de la respuesta, no como una excepción — y el código anterior no distinguía ese caso de una tabla realmente vacía. Cuando el walk vencía por timeout, el bucle terminaba igualmente con “0 entries” y el código lo trataba como una lectura válida (tabla vacía de verdad), escribiendo 0 sin que el equipo hubiera respondido nunca. Es exactamente el patrón que la Regla 13 del proyecto prohíbe: un dato con apariencia real que en verdad es la ausencia de dato.

El segundo síntoma tenía una causa distinta pero relacionada: el tope de 7 s (SNMP_HARD_TIMEOUT) se aplicaba al sondeo de extras COMPLETO (varios GET + un walk, secuenciales), no a cada petición por separado. Un solo host muerto agotaba ese tope antes de que el sondeo llegara a terminar ninguna de sus peticiones.

Fix aplicado

Commit 2802de577ffd54f23ab07ab2ca5d04adac9dae39 (PR #434, Agente 2.21.5, SaaS v1.83.5):

  • Nueva función _is_timeout(error_indication) en terminal/agent/sentinel/snmp_extras.py que reconoce el timeout de pysnmp dentro de la respuesta.
  • snmp_poll_oids() ya solo escribe un valor si el equipo contestó de verdad: variable answered que se marca True solo tras una respuesta real del walk; si nunca se marca, el walk se descarta sin escribir nada (ni 0 ni cualquier otro valor).
  • El tope duro deja de envolver el sondeo entero: ahora SNMP_HARD_TIMEOUT (7 s) se aplica DENTRO de snmp_poll_oids, a cada GET/walk individual vía el helper _request(), y el primer silencio aborta el resto del sondeo (unresponsive = True) — un host muerto cuesta ~5 s en vez de 27 s.
  • Nueva constante SNMP_POLL_HARD_TIMEOUT = 30 (terminal/agent/config.py) que envuelve el sondeo completo como red de seguridad, para que solo salte ante una anomalía real y no ante el caso normal de un host sin respuesta.
  • Cierra un hilo que había quedado abierto como “límite honesto” en el hotfix 2.21.4 (¿por qué pysnmp no honra su timeout?): sí lo honra — 5,66 s medidos —; lo que no cabía en el tope de 7 s era la suma de varias peticiones secuenciales, no una petición individual colgada.
  • Test nuevo: tests/agent/test_snmp_poll_no_fabricated_zero.py.
  • Verificado contra el Xirrus de la oficina (15 métricas, comportamiento igual) y contra hosts sin ruta (5,4 s, None). Sin verificar todavía en la red real del CCIB — la VPN estaba caducada en el momento del fix; verificación pendiente hasta septiembre. Tasks #262/#263 siguen abiertas.

Lecciones

  • Un timeout vencido y una respuesta vacía real son dos cosas distintas y hay que distinguirlas explícitamente — tratarlas igual fabrica un dato donde no lo hay, y ese dato fabricado puede además contaminar las señales de salud que deberían haber detectado el problema (aquí, el contador de cuelgues se reiniciaba con cada “0” falso).
  • Un tope duro aplicado al sondeo AGREGADO (varias peticiones secuenciales) penaliza igual un sondeo con un host muerto que uno con todos muertos, y puede cortar el trabajo antes de que termine ni la primera petición. El tope debe vivir al nivel de la unidad que realmente puede colgarse (una petición), con un tope agregado aparte solo como red de seguridad ante anomalías.
  • Verificar con datos reales cierra hilos que de otro modo quedan como suposición: aquí, medir 5,66 s confirmó que pysnmp sí respeta su timeout, descartando una hipótesis que llevaba abierta desde el hotfix anterior.

Preventivos futuros

  • Verificación pendiente contra la red real del CCIB en septiembre, cuando la VPN se renueve — este fix está validado contra otros equipos (Xirrus de oficina, hosts sin ruta) pero no contra el entorno que originó el problema.
  • Vigilar tras el despliegue: que snmp_fast_station_count no vuelva a mostrar 0 sostenido en APs con histórico de respuesta, y que las métricas “extras” del CCIB vuelvan a poblarse con datos.
  • Tasks #262/#263 (abiertas) son el lugar donde seguir el hilo de fiabilidad del motor SNMP más allá de este hotfix puntual.

Véase también

  • [[incident—20260824—snmp-motor-autocuracion-diagnostico-erroneo]]
  • [[incident—20260824—snmp-motor-corrupto-tras-cuelgues-seguidos]]
  • [[incident—20260824—breaker-mixto-silencia-snmp-ccib]]
  • [[crearack-tech—agents—dev-terminal]]
  • [[crearack-tech—architecture—sentinel-mode]]