Funciones principales
build_state_context(sections, user) → str
Orquesta la construcción del contexto. Itera las secciones solicitadas, llama a cada builder privado y concatena los resultados en un bloque [CONTEXTO …]. Desde v1.119.0 antepone al bloque el aviso de _agent_disconnection_notice cuando aplica (ver abajo).
_agent_disconnection_notice(org) → str (v1.119.0 · task #293 · corregida en v1.132.13 · task #309)
Problema que resuelve: el bloque de contexto se etiquetaba “datos en vivo del sistema” aunque el Agente del cliente llevara horas mudo — los conteos de alerts/devices se presentaban sin avisar de su antigüedad.
Decisión de producto (Edu): avisar, no ocultar. Se antepone una línea de aviso si el último Agente visto de la organización lleva status="offline" más de 30 minutos (mismo umbral que racks/services/integrity.py). Umbral: _AGENT_STALE_THRESHOLD_MINUTES = 30.
Lógica (v1.132.13): si algún AgentInstance de la org está online, no hay aviso; si no, se toma el de last_seen más reciente sin filtrar por rol. Por qué: la versión de v1.119.0 filtraba role="primary", y el failover degrada a secondary al Agente que se desconecta — en el instante en que el aviso empezaba a hacer falta ya no quedaba ningún “primary” que mirar. Con un solo Agente por organización (las dos reales) el aviso era imposible por construcción (medido en PROD el 13-09-2026: cadena vacía en ambas orgs con el Agente 15 h mudo; cronología real: desconexión 12-09 13:29, failover primary→secondary 13:30).
Salida: "(el Agente de monitorización lleva {N}h desconectado — estos datos pueden estar desactualizados)", insertada tras la cabecera [CONTEXTO: ...] y antes de las líneas de sección.
_build_alerts_context(user, is_admin) → str
⚠️ Corregida en s56 / commit
06a228a— ver [[incident—20260511—informante-alerts-conteo-incorrecto]]. Y en v1.132.13 (task #309): la fuente 2 pasa delast_statuscrudo astatus_effective.
Agrega 3 fuentes operativas de alertas:
| # | Fuente | Modelo | Filtro |
|---|---|---|---|
| 1 | AlertEvent activos | AlertEvent | alert__organization=org, resolved_at__isnull=True |
| 2 | Devices caídos | MonitoringTarget | organization=org y status_effective == "down" (property Python: se itera el queryset, dataset pequeño — mismo patrón que monitoring/api/targets.py) |
| 3 | Insights IA pendientes | AIInsight | organization=org, status in [PENDING, EXECUTING] |
El resumen devuelto etiqueta explícitamente cada categoría:
Alertas activas: 133 (devices caídos: 128, alertas configuradas: 5, insights IA pendientes: 0).
Top devices caídos: sw-core1, sw-core2, ... (+123 más sin detallar).
El campo is_admin controla si se listan los top devices caídos y las alertas configuradas individuales (los viewers solo reciben el conteo total). El texto de cero alertas ya no afirma “todos los devices en up”: dice “ningún device caído con medida reciente”.
Antes de s56 (comportamiento incorrecto): solo contaba AlertEvent. Devices caídos e insights IA se ignoraban, causando que el Informante respondiera “0 alertas activas” con 128 devices caídos en PROD.
_build_devices_context(user, is_admin) → str
Resumen de MonitoringTarget del tenant por status_effective (v1.132.13 · task #309): total, online, offline y “sin dato reciente” como categoría propia — Devices: 3 (online: 1, offline: 1, sin dato reciente: 1).; si ninguno tiene medida reciente, Devices: N, ninguno con dato reciente (sin medida del Agente de monitorización). sin lista de caídos. Para admins lista los 5 primeros devices caídos (con medida reciente) con IP. Desde s56 añade el sufijo (+N más sin detallar) cuando la lista está truncada, para que el modelo entienda que es parcial.
Antes de v1.132.13 (comportamiento incorrecto): contaba por last_status crudo, que no caduca. Con el Agente de la org 2 mudo 15 h, metía en el prompt “Devices: 187 (online: 117, offline: 70). Caídos: 172.25.10.204; …” con la instrucción de no inventar números, cuando el criterio del resto de la app daba {'unknown': 187}. La migración a status_effective de la task #277 llegó a 15 sitios del monitoring y este fichero se quedó fuera (su propio comentario lo reconocía).
_build_signage_context(user, is_admin) → str
Resumen del estado de Digital Signage del tenant. Desde v1.119.0 (task #296.1) ya NO consulta ContentDeployment: la tabla tiene 0 filas en las 6 organizaciones de PROD y nadie la escribe desde que se retiró su endpoint en v1.38.1 — el bloque de “despliegues fallidos 24h” era código muerto que solo podía devolver silencio. El resumen de reproductores (mismo builder) no se toca.
_build_health_context(user, is_admin) → str
Tres checks operacionales, sin modelo propio: ping a la base de datos (siempre), espacio libre en disco y uptime del proceso (estos dos últimos solo para admins, por ser info sensible de infra).
Uptime corregido en v1.126.0 (2026-09-06 · task #293 pt.2): antes restaba la fecha de modificación (mtime) del propio fichero help_state.py, es decir la fecha del build de la imagen. Tras un reinicio del proceso sin deploy nuevo el uptime mostrado no se movía, y justo después de un deploy marcaba “0h” aunque el proceso llevara horas arriba. Ahora se mide desde _PROCESS_STARTED_AT, una constante de módulo fijada al importarse — y core/apps.py importa help_state en ready() para que ese instante sea el arranque real del worker, no la primera pregunta que alguien le hace al Informante.
Aislamiento multi-tenant
- Todo filtro incluye
organization=orgexplícito. - El
TenantRLSMiddlewareaplica RLS Postgres, peroAlertEventno tiene FK directa aOrganization(se filtra víaalert__organization), por lo que el RLS no es suficiente por sí solo. MonitoringTargetyAIInsightsí tienen FK directa aOrganizationy aprovechan RLS + filtro explícito.
Tests
Los tests de integración viven en tests/api/test_help.py. Casos relevantes añadidos en s56:
test_build_context_with_targets_down— devicelast_status=downconlast_checkreciente → cuenta como alerta (desde v1.132.13 el fixture fijalast_check: sin medida reciente sería “sin dato reciente”).test_build_context_with_pending_insight—AIInsight.Status.PENDING→ cuenta como alerta.test_build_context_no_active_alerts— las 3 fuentes vacías →"Alertas activas: 0".
Añadidos en v1.119.0 (tests/api/test_help.py): aviso de agente desconectado (>30 min offline), ausencia de aviso con agente online o sin last_seen, y retirada del bloque ContentDeployment de _build_signage_context.
Añadidos en v1.126.0 (tests/network/test_ronda_0906_tanda5.py — sí, viven junto al test del backfill de firmware, no en tests/api/test_help.py): test_uptime_counts_from_process_start (mockea _PROCESS_STARTED_AT y comprueba el texto “uptime ~2h5m”) y test_uptime_is_hidden_from_non_admins.
Añadidos en v1.132.13 (tests/api/test_help_state_agent_mute_309.py): todo caducado → “ninguno con dato reciente” sin “Caídos” y CON aviso aunque el Agente sea secondary offline; mezcla fresco/caducado → online/offline/sin dato reciente; aviso con secondary degradado; sin aviso si alguno está en línea; sin aviso bajo el umbral.
Historial de cambios relevantes
| Versión | Cambio |
|---|---|
| v1.132.13 (2026-09-15) | Task #309 (ronda 13-09): _build_devices_context/_build_alerts_context cuentan por status_effective con la categoría “sin dato reciente”; _agent_disconnection_notice deja de exigir role="primary" (era imposible por construcción con un solo Agente). PR #543. |
| v1.126.0 (2026-09-06) | _build_health_context: el uptime del proceso deja de medir la mtime del fichero y pasa a medir _PROCESS_STARTED_AT, fijada al importar el módulo desde core/apps.py.ready() (task #293 pt.2). |
| v1.119.0 (2026-09-06) | _agent_disconnection_notice (task #293): avisa en el bloque de contexto si el Agente primary lleva >30 min offline, sin ocultar los números. _build_signage_context deja de consultar ContentDeployment (task #296.1, tabla muerta desde v1.38.1). |
| s56 (2026-05-11) | _build_alerts_context amplía a 3 fuentes; _build_devices_context añade tail “+N más”. |
| Anterior | _build_alerts_context solo contaba AlertEvent → bug en PROD (ver [[incident—20260511—informante-alerts-conteo-incorrecto]]). |
Véase también
- [[incident—20260511—informante-alerts-conteo-incorrecto]] — incidente PROD que motivó el fix de s56
- [[entity—monitoring—model—alertevent]] — modelo AlertEvent (fuente 1 del contexto de alertas)
- [[entity—monitoring—model—monitoringtarget]] — modelo MonitoringTarget (fuente 2)
- [[entity—monitoring—model—aiinsight]] — modelo AIInsight / CNS Sentinel (fuente 3)
- [[crearack—redes-infra—monitorizacion]] — visión general del módulo de monitorización
- [[entity—monitoring—model—monitoring-target]] — documenta
status_effective, el criterio de frescura que motivó el aviso de task #293