Los clientes WiFi que ves son los de ahora (task #225, v1.66.9)
Síntesis
La página Wireless presentaba como clientes actuales los datos de un análisis profundo (deep_snmp_data) realizado hace semanas. En PROD, de 3.219 clientes reportados, 3.203 eran de APs caídos desde el 30 de junio. El “Busiest AP” señalaba hace 6 semanas al mismo punto.
Ahora: solo cuentan los clientes de APs que el MonitoringTarget ve vivos (last_status == "up") en este momento. La cifra cae, pero la UI expresa “N of M APs reporting live” para dejar claro que no es avería.
Evolución v1.85.2 (ver sección más abajo): desde el 25-08-2026 el juez de “vivo” ya no depende solo del ping — también mira SNMP en vivo. Esta sección de Síntesis describe el mecanismo ORIGINAL (v1.66.9); sigue siendo el criterio de respaldo.
Cambio funcional
Nuevas funciones en monitoring/api/wireless.py
_has_current_clients(profile) → bool
- Propósito: ¿Los clientes de este AP reflejan el AHORA?
- Lógica:
profile.linked_monitoring_target and mt.last_status == "up" - Fuente:
MonitoringTarget.last_status(señal viva del target) - Nota: Más estricto que
_ap_status()a propósito — ésta llama “online” a un AP sin target solo por tener deep data, lo que era la puerta por la que entraba la foto vieja.
_current_client_count(profile) → int
- Propósito: Clientes del AP, o 0 si el dato no es del ahora.
- Lógica: Cuenta via
_count_clients_from_deep()solo si_has_current_clients()es verdad. - Fuente:
profile.deep_snmp_data(JSON con estructura{wireless: [{name: "clientsPerRadio", value: "N"}]}).
Lectores afectados (5)
Todos reemplazan la lectura directa de deep_snmp_data por _current_client_count(p) / _has_current_clients(p):
-
get_wireless_summary(): endpoint/api/monitoring/wireless/summary- Nuevo:
aps_reporting_clients,aps_without_current_data avg_clients_per_apse calcula sobre los que reportan, no sobre la flota entera- Busiest AP solo entre los vivos
- Nuevo:
-
get_wireless_status_list():/api/monitoring/wireless/status-list- Zeroing de
client_countpara APs sin datos vivos - Crítico porque el dashboard suma estos valores por su cuenta (
WirelessDashboard.computeMetrics)
- Zeroing de
-
get_wireless_report(): informe individual por APclient_count: 0si el AP está caído
-
get_wireless_fleet_report():/api/monitoring/wireless/fleet-report- Nuevo:
aps_reporting_clients,aps_without_current_data - Cada AP en fleet trae
clients: 0si es stale
- Nuevo:
-
get_wireless_group_summary()— cerrado el 23-08-2026 en task #255 (pieza 3, v1.82.5): la tarjeta de grupo se quedó fuera del arreglo original y seguía sumando la última foto como si fuera de ahora. Ahora usa el mismo juez que el resumen general; la agregación de SSIDs (_aggregate_ssids) también se salta a los APs sin dato vivo.
Frontend
-
Template:
templates/monitoring/wireless.html- Nuevo div:
metric-sublabelbajo “Total Clients”
- Nuevo div:
-
Stylesheet:
static/css/pages/wireless.css- Clase
.metric-sublabel: 10px, color muted, margin-top 2px, line-height 1.3
- Clase
-
JS:
static/js/pages/wireless/WirelessDashboard.jsupdateSummaryCards()llena#wl-metric-clients-sourcecon “N of M APs reporting live” (interpolated)- Solo se pinta desde la API;
updateMetrics()(local) no lo toca
Contrato de tests
5 tests en tests/api/test_monitoring.py::TestWirelessClientFreshness:
test_summary_ignores_clients_of_dead_aps: AP up (5 clientes) + AP down (200 en foto) → total = 5test_busiest_ap_is_never_a_stale_photo: Busiest AP es siempre del que está vivotest_status_list_zeroes_stale_client_counts: status-list devuelve 0 para dead APstest_ap_without_monitoring_target_does_not_count: Sin target no hay señal de vida (huérfano → 0)test_fleet_report_excludes_stale_and_reports_how_many: Fleet report cuenta y expone stales
Deuda — CERRADA
La deuda original decía que deep_snmp_data no lleva fecha en ninguna parte (ni en el JSON del Agente, ni como campo del modelo, ni en last_updated) y que por eso el fix de esta página no podía decir cuándo era la foto, solo dejar de sumarla.
Esa deuda quedó cerrada por v1.66.16 (migración network/0060, campo DeviceProfile.deep_snmp_updated_at) — ver [[feature—wireless—deep-discovery-timestamp]]. El 23-08-2026 (task #255) se corrigió además el comentario del código que seguía afirmando la versión vieja (“no lleva fecha en ninguna parte”), que había quedado rancio desde v1.66.16 sin que nadie lo tocara.
Impacto en observabilidad
- Métrica que cambia:
total_clients(baja dramáticamente de ~3200 a ~16 en este caso) - Métrica nueva:
aps_reporting_clients/aps_without_current_data(desambigua si es avería o verdad) - User feedback: Sublabel “2 of 124 APs reporting live” explica la caída
Evolución — v1.85.2 (ciclos 5 y 6 de la auditoría #261): el juez ahora también mira SNMP vivo
El juez original de esta página (_has_current_clients / _current_client_count) solo confiaba en MonitoringTarget.last_status == "up", es decir, en el ping. En el CCIB, donde la red filtra ICMP hacia los APs, eso hacía que 47 puntos de acceso con SNMP perfectamente vivo salieran en rojo y sin clientes (W-H1, W-H2).
WirelessLive (monitoring/api/wireless.py)
Nueva clase que hace una sola consulta instantánea a VictoriaMetrics por petición (last_over_time(...) sobre snmp_fast_station_count / snmp_extras_connected_clients / snmp_fast_connected_clients, ventana de 2× la cadencia del target + margen, mínimo 3 min) para saber qué targets tienen una muestra SNMP reciente y cuántos clientes reportan ahora mismo.
WirelessLive.for_profiles(org, profiles): construye la instancia para un lote de perfiles de una tirada, víaMetricsReader(monitoring/services/metrics_reader_base.py)._ap_status(profile, live): online si el ping ve el AP arriba O silive.is_fresh(profile)(SNMP fresco) — antes solo dependía del ping._has_current_clients(profile, live)/_current_client_count(profile, live): si hay SNMP fresco, el recuento sale directo delive.clients(dato en vivo); si no, cae al criterio de más arriba en esta página (ping arriba → foto del deep discovery); si tampoco, 0. La foto queda como respaldo, no como fuente principal.- Sin VictoriaMetrics o sin targets,
WirelessLivequeda vacía y el comportamiento es idéntico al descrito en el resto de esta página (juez = ping).
A42 · /wireless/deep-data con el mismo juez
Este endpoint tenía su PROPIO cálculo de client_count (sumaba clientsPerRadio de la foto sin mirar frescura) y el flujo “Deep Data” del frontend lo escribía en ap.client_count: una regresión de la #225 colándose por otra puerta. Ahora llama a _current_client_count(p, live), igual que el resumen.
Otros arreglos del mismo ciclo (misma familia de honestidad de datos)
- W-H4:
firmware_versionse rellena desde la foto del deep discovery (firmwareVersion/cdpFirmware) en el ingest — estaba vacío en 249/249 APs. - W-H6: la ficha del AP dice “Deep data: undated snapshot” cuando la foto es anterior al sello
deep_snmp_updated_at(ver [[feature—wireless—deep-discovery-timestamp]]), en vez de callar justo en las fotos más viejas. - A43: el sweep de flota usa el perfil de vendor
genericcuando no reconoce el equipo (antes lo saltaba en silencio).
Commit 1195434d (PR #439, v1.85.2). Tests: tests/monitoring/test_ciclos5_6_wireless_seguridad.py (9). Sin verificar en la red del CCIB hasta septiembre.
Evolución — v1.85.9 (deuda #274, B1-#12): el conteo por _count_clients_from_deep ya no sale multiplicado
_count_clients_from_deep() — la función que _current_client_count() llama cuando no hay SNMP fresco (ver la sección “Evolución v1.85.2” de arriba) — caía a contar la lista clients de la foto de deep discovery entrada por entrada cuando el AP no traía clientsPerRadio. Esa lista es plana: una fila por OID × cliente, no una fila por cliente. Con 8 OIDs de cliente declarados, el conteo salía ×8. Ahora cuenta index únicos dentro de clients, que sí es un cliente real por fila. Detalle completo de la tanda: [[feature—monitoring—deuda-274-tanda-a]].
Evolución — v1.164.5 (28-09-2026, visto en PROD durante FABCON26): la agregación de SSIDs también exige que la FOTO sea reciente
El arreglo de task #255 (sección “Lectores afectados” de arriba) hizo que get_wireless_group_summary() dejara fuera de la agregación de SSIDs a los APs sin dato vivo — pero un AP “vivo” (con SNMP o ping recientes) puede llevar semanas sin repetir su Deep Discovery, porque esa foto no se renueva sola. En el grupo “CCIB&Forum” (124 APs) esto dejaba colarse en la tabla de SSIDs redes de eventos ya terminados (MorganMoney, PXP26 WORLD TOUR) por delante de la red del evento en curso: el filtro miraba si el AP estaba vivo, no si la FOTO lo estaba, y 99 de las 124 fotos eran del 16-09.
_ssid_photo_is_recent(profile, now) añade ese segundo filtro: a la agregación solo entra un AP cuya deep_snmp_updated_at tenga menos de SSID_PHOTO_MAX_AGE (60 min, UNVERIFIED — elegido por criterio, no medido). La respuesta de get_wireless_group_summary suma ssids_from_aps (APs cuya foto entró), ssids_old_aps (APs vivos dejados fuera por foto vieja), ssids_as_of (la foto más vieja de las que sí cuentan) y ssids_max_age_minutes; WirelessGroupDetail.js los usa para decir de cuántos APs sale la tabla o, si ninguna foto es reciente, pedir que se relance Deep Discovery en vez de no mostrar nada.
El total de clientes del grupo (total_clients) no cambia — ya salía del dato en vivo desde v1.85.2; este filtro solo afecta a qué fotos entran en la tabla de SSIDs por nombre. Tests: tests/monitoring/test_wireless_grupo_ssids_foto.py (4). Commit 602638f7 (PR #623).
Véase también
- [[entity—monitoring—model—monitoring-target]]
- [[entity—network—model—device-profile]]
- [[concept—monitoring—wireless-observability]]
- [[feature—wireless—deep-discovery-timestamp]]
- [[feature—ups—graficas-vivas-walk-first]]
- [[crearack-tech—architecture—wireless-monitor]]
- [[feature—monitoring—deuda-274-tanda-a]]