Agente · dev-observatory
Propósito
Desarrollo y mantenimiento del Network Observatory de CreaRack-Pro.
Cubre la app monitoring/, los submódulos JS de Observatory y la integración con VictoriaMetrics.
Módulos JS (pages/observatory/)
static/js/pages/
├── observatory.js # Coordinador principal (~430 líneas)
└── observatory/
├── ObservatoryCharts.js # Funciones de gráficas ECharts
├── ObservatoryCNS.js # Integración CNS / Edge Intelligence
├── ObservatoryOverview.js # Dashboard overview + Agent Fleet widget
├── ObservatorySentinel.js # Sentinel Mode UI
├── ObservatoryTabs.js # Gestión de pestañas + formatRangeLabel()
├── ObservatoryAlerts.js # Sistema de alertas UI
├── ObservatoryWebSocket.js # WebSocket handler
├── ObservatorySettings.js # Modales de configuración SNMP/HTTP
└── PingConsole.js # Consola de ping en tiempo real
App Django: monitoring/
monitoring/
├── models.py # MonitoringTarget, MetricSample, AggregatedMetric,
│ # MonitoringAlert, AlertEvent
├── api.py # Endpoints Django Ninja (~87 endpoints total)
├── views.py # observatory_view (inyecta devices desde Django)
├── consumers.py # MonitoringConsumer (WebSocket)
├── routing.py # WebSocket routing
└── services/
├── ping_service.py # PingService (icmplib)
├── snmp_service.py # SNMPService — bandwidth + SNMPv3
├── http_service.py # HttpService — health checks
└── alert_service.py # Evaluación de alertas
Modelos de datos
MonitoringTarget
Dispositivo/IP a monitorear. Contiene configuración por tipo.
ping_enabled # Bool
snmp_enabled # Bool
http_enabled # Bool
config # JSONField — credenciales SNMPv3, URL HTTP, etc.
SNMPv3 en config:
{
"snmp_version": "v3",
"snmp_v3_username": "...",
"snmp_v3_auth_protocol": "SHA256",
"snmp_v3_auth_key": "...",
"snmp_v3_priv_protocol": "AES128",
"snmp_v3_priv_key": "..."
}
MonitoringAlert
is_global— aplica a TODOS los targets de la organizaciónorganizationFK — permite consultas sin necesitar target- Condiciones soportadas:
latency_above,packet_loss_above,down_for,bandwidth_above,http_response_time_above,http_status_error,http_status_not_ok
AlertEvent
targetFK — rastrea qué dispositivo disparó una alerta global
MetricSample / AggregatedMetric
- LEGACY desde v1.68.1 (task #230): el histórico vive en VictoriaMetrics (vía Agente).
MetricSamplequeda SOLO como caché de contadores SNMP (bandwidth_in/out) para el cálculo de caudal server-side;AggregatedMetricsin lector ni escritor (DROP diferido a ciclo de migración propio — footgun RLS/DROP POLICY) - Limpieza: >30 días samples, >90 días alertas resueltas (
cleanup_metricssigue vigente)
API Endpoints — Core (targets, metrics, alerts, batch)
Targets
| Método | URL | Descripción |
|---|---|---|
| GET/POST | /api/monitoring/targets | CRUD targets |
| PATCH | /api/monitoring/targets/{id}/config | Actualizar solo configuración |
| POST | /api/monitoring/devices/{id}/enable-monitoring | Crear target desde device |
Métricas
| Método | URL | Descripción |
|---|---|---|
| POST | /api/monitoring/targets/{id}/ping | Ejecutar ping |
| POST | /api/monitoring/targets/{id}/snmp/poll | Poll SNMP |
| POST | /api/monitoring/targets/{id}/http/check | HTTP health check |
Retirados en v1.68.1 (task #230):
GET {id}/metrics·GET {id}/stats·GET {id}/history·GET {id}/bandwidth— leían tablas PG vacías (y/statsfabricaba un 0% falso). El histórico se sirve porGET {id}/vm/*(VictoriaMetrics). Test que lo fija:tests/monitoring/test_pg_metrics_retired.py.
Batch (concurrencia)
POST /api/monitoring/targets/batch-ping # 20 concurrentes max
POST /api/monitoring/targets/batch-snmp # 10 concurrentes max
POST /api/monitoring/targets/batch-http # 15 concurrentes max
Alertas
| Método | URL | Descripción |
|---|---|---|
| POST | /api/monitoring/alerts/global | Crear alerta global (target=NULL) |
| GET | /api/monitoring/alerts?target_id=X | Per-device + globales (Q objects) |
| POST | /api/monitoring/alerts/{id}/acknowledge | Reconocer alerta |
| GET | /api/monitoring/alerts/active | Alertas activas sin resolver |
WebSocket
// Conexión
const ws = new WebSocket('ws://localhost:8000/ws/monitoring/');
// Suscribirse
{"type": "subscribe", "target_ids": [1, 2, 3]}
// Server → Client: actualización de métricas
{
"type": "metric_update",
"target_id": 1,
"metrics": {"status": "up", "latency_ms": 15.2, "packet_loss": 0}
}
// Server → Client: alerta disparada
{
"type": "alert_triggered",
"alert_id": 5,
"target_id": 1,
"value": 150.5
}
Handler: ObservatoryWebSocket.js — gestiona suscripciones y despacha eventos a los módulos correspondientes.
ECharts — arquitectura centralizada
Regla fundamental: Todas las gráficas pasan por EChartsService.js. Un cambio en EChartsService afecta las 3 gráficas (Heartbeat, Bandwidth, HTTP) de todos los dispositivos.
Configuración compartida via EChartsService
| Qué | Método | Efecto |
|---|---|---|
| Grid layout | getBaseOptions() | Márgenes idénticos (left:65, right:65, top:40, bottom:65) |
| Tooltip | getBaseOptions() | Formato es-ES, crosshair, estilo oscuro |
| DataZoom | getDataZoom() | Slider inferior + zoom con rueda |
| Threshold lines | getThresholdMarkLines() | Colores por severidad |
| Threshold areas | getThresholdMarkAreas() | Zonas sombreadas de alerta |
Sincronización entre gráficas
// ObservatoryCharts.js — conectar las 3 gráficas de un dispositivo
echarts.connect([heartbeatChart, bandwidthChart, httpChart]);
Sincroniza: hover/tooltip cruzado + DataZoom porcentual. NO sincroniza: escala yAxis, series data, zoom state en localStorage.
Patrón de actualización — SIEMPRE así
// Solo actualizar series — nunca tocar xAxis/yAxis
chart.setOption({ series: [{ data: newData }] });
⚠ Nunca modificar
xAxisniyAxisen funciones de update — rompe la sincronización entre gráficas.
Controles globales por dispositivo
| Control | Método | Afecta |
|---|---|---|
| Range (01m/1h/6h/24h) | setGlobalTimeRange() | Re-fetch en las 3 gráficas |
| Chart Style | EChartsService.setChartStyle() | Estilo visual en las 3 |
| Animation (Ani) | EChartsService.toggleAnimation() | Pulso visual on/off |
| Alert Lines (AL) | EChartsService.toggleMarkLines() | Threshold lines on/off |
| Pause/Resume | EChartsService.pauseDeviceCharts() | Pausa/reanuda las 3 |
Colores por defecto
// Heartbeat
latency: '#3b82f6' // Azul
packetLoss: '#ff9800' // Naranja
// Bandwidth
inbound: '#4caf50' // Verde
outbound: '#2196f3' // Azul
aggregate: '#ff9800' // Naranja (dashed, hidden by default)
// HTTP
response: '#10b981' // Esmeralda (visualMap: verde→amarillo→rojo)
Selector de tiempo — ciclo de clicks
| Botón | Ciclo |
|---|---|
01m | 01m → 05m → 10m → 20m |
1h | 1h → 2h → 4h → 8h |
6h | 6h → 12h → 24h → 48h |
24h | 24h → 48h → 72h → 7d |
formatRangeLabel() centralizado en ObservatoryTabs.js.
Backend acepta hours: float en todos los endpoints.
Funcionalidades especiales
Asteroids Alert Mode
- Canvas de asteroides cambia a rojo cuando hay alertas críticas activas
- Persistencia cross-page (localStorage) y cross-tab (storage event)
- Se restaura al archivar todas las alertas críticas
Ping Console
- Panel tipo terminal en sección Heartbeat (360px ancho)
- Pings en tiempo real cada 2 segundos
- Colores: verde (success), rojo (timeout), gris (info)
- Límite 50 líneas con scroll automático
- Gráfica se desplaza a la derecha al activar la consola
Alertas globales
MonitoringAlert.is_global = Trueaplica a todos los targets- Badge
[Global]en UI, grosor 1.5px, dash[6, 4]en threshold lines switchTab()refresca threshold lines al activar tab
Escrituras async a VictoriaMetrics (fire-and-forget)
MetricsWriter._fire_and_forget()— escrituras no bloqueantes- Contexto async (Daphne):
loop.create_task() - Contexto sync (Ninja views):
threading.Thread(daemon=True) - Errores de VM se loguean sin afectar la respuesta HTTP
Draggable sidebar
- Ancho: 280–560px, persistido en localStorage
- Drag handle para redimensionar
Management commands
# Ping automático continuo (cada 10s)
docker compose exec web python manage.py ping_targets --continuous --interval 10
# Limpieza de datos (>30 días samples, >90 días alertas)
docker compose exec web python manage.py cleanup_metrics
# aggregate_metrics RETIRADO en v1.68.1 (task #230): su salida (AggregatedMetric) se quedó sin lector
OIDs SNMP soportados
ifDescr(1.3.6.1.2.1.2.2.1.2)ifSpeed(1.3.6.1.2.1.2.2.1.5)ifOperStatus(1.3.6.1.2.1.2.2.1.8)ifInOctets/ifHCInOctetsifOutOctets/ifHCOutOctetsifInErrors/ifOutErrors
VictoriaMetrics — arquitectura de métricas
Stack de datos (roles complementarios)
| Tecnología | Rol | Retención | Velocidad |
|---|---|---|---|
| Valkey | Cache, sesiones, pub/sub Django Channels | Segundos a horas | Milisegundos |
| VictoriaMetrics | Series temporales Observatory + infra SaaS | 180 días | ~100ms |
| PostgreSQL | Datos de negocio (targets, alertas, config) | Permanente | ~10-50ms |
Multi-tenancy — separación por labels
# SIEMPRE incluir tenant_id en cada métrica
{
"__name__": "ping_latency_ms",
"tenant_id": "5", # ← separación entre clientes
"target_id": "123",
"target_ip": "192.168.1.1"
}
Seguridad — filtrar SIEMPRE por tenant
# En cada endpoint de métricas — nunca omitir tenant_id
tenant_id = request.auth.user.tenant_id
query = f'ping_latency_ms{{tenant_id="{tenant_id}", target_id="{target_id}"}}'
return await MetricsReader.query_range(query, hours)
Escrituras fire-and-forget
# No bloqueante — el usuario no espera confirmación de VM
MetricsWriter._fire_and_forget()
# Contexto async (Daphne): loop.create_task()
# Contexto sync (Ninja views): threading.Thread(daemon=True)
Consultas PromQL desde Django
# Ejemplo: latencia media últimas 24h de un target
query = f'avg(ping_latency_ms{{tenant_id="{tid}", target_id="{tid}"}})[24h]'
result = await MetricsReader.query_range(query, hours=24)
Acceso a VictoriaMetrics en producción
Solo accesible desde dentro de la red Docker — no expuesto externamente:
# Consulta via contenedor Django
ssh root@crearack.com "docker exec crearack-pro-zcmvsl-web-1 python -c \
\"import urllib.request, urllib.parse, json; \
q = urllib.parse.quote('<PromQL query>'); \
r = urllib.request.urlopen(f'http://victoriametrics:8428/api/v1/query?query={q}'); \
print(json.loads(r.read()))\""
UI local (con compose.observability.yml): http://localhost:8428
API Endpoints — Modulos especializados (130+ endpoints)
Endpoints de monitoring/ no cubiertos en la seccion principal.
ITSM (/api/monitoring/sentinel/itsm/) — 26 endpoints
| Metodo | Endpoint | Proposito |
|---|---|---|
| GET/PUT | /sla/policies, /sla/policies/{risk_level} | CRUD politicas SLA (HIGH/MEDIUM/LOW) |
| POST | /sla/initialize | Inicializar SLA por defecto |
| GET | /sla/metrics | Metricas MTTA/MTTR (30 dias default) |
| GET/POST/PUT/DELETE | /notifications/channels, /{id} | CRUD canales notificacion |
| POST | /notifications/channels/{id}/test | Enviar notificacion test |
| GET | /notifications/log | Ultimas 50 notificaciones |
| GET/POST/PUT/DELETE | /escalation/policies, /{id} | CRUD politicas escalamiento |
| GET/POST/PUT/DELETE | /maintenance, /{id} | CRUD ventanas mantenimiento |
| GET | /maintenance/active | Ventanas activas ahora |
| GET | /analytics | Dashboard analitica ITSM (30d) |
| POST/DELETE | /links, /{id} | Crear/eliminar links entre insights |
| GET | /insights/{id}/links | Links de un insight |
| GET | /patterns | Patrones recurrentes detectados |
| POST | /patterns/{id}/acknowledge | Marcar patron como conocido |
ITSM Extendido — 16 endpoints
| Metodo | Endpoint | Proposito |
|---|---|---|
| GET | /sentinel/itsm/groups | Listar grupos de incidentes |
| GET | /sentinel/itsm/groups/{id} | Detalle grupo |
| POST | /sentinel/itsm/groups/{id}/resolve | Resolver grupo |
| POST | /sentinel/itsm/groups/{id}/acknowledge-all | Reconocer todos los insights |
| GET/POST/PUT/DELETE | /sentinel/itsm/known-issues | CRUD known issues |
| POST | /sentinel/itsm/known-issues/from-insight/{id} | Crear known issue desde insight |
| GET/POST/PUT/DELETE | /sentinel/itsm/runbooks | CRUD runbooks |
| POST | /sentinel/itsm/runbooks/{id}/apply/{insight_id} | Aplicar runbook a insight |
| GET | /sentinel/itsm/reports/{id} | Informe JSON de un insight |
| GET | /sentinel/itsm/reports/{id}/markdown | Informe Markdown descargable |
CNS Insights (/api/monitoring/sentinel/insights/) — 15 endpoints
| Metodo | Endpoint | Proposito |
|---|---|---|
| POST | /receive | Recibir anomalia del Agent → generar insight (JWT) |
| GET | / | Listar insights (filtros: status, risk, target, search, fechas) |
| GET | /stats/ | Estadisticas por estado |
| GET | /stats/providers/ | Uso por proveedor IA |
| GET | /{id}/ | Detalle insight |
| POST | /{id}/acknowledge | Reconocer con notas opcionales |
| POST | /{id}/explain | Pregunta contextual sobre insight (async) |
| POST | /recover/{target_id} | Target recuperado → auto-resolver (JWT) |
| DELETE | /purge | Eliminar TODOS los insights (superuser) |
| GET | /{id}/conversations | Historial Q&A del insight |
| POST | /{id}/revise | Re-evaluar diagnostico con historial |
| POST | /{id}/apply | Aplicar comandos via Agent |
| POST | /{id}/dry-run | Validar sin ejecutar |
| POST | /{id}/rollback | Rollback de insight aplicado |
| POST | /{id}/result | Recibir resultado de ejecucion (JWT) |
Wireless Monitor (/api/monitoring/wireless/) — 18 endpoints
| Metodo | Endpoint | Proposito |
|---|---|---|
| GET | /summary | Stats agregados WiFi (online, offline, clients) |
| GET | /status-list | Estado por AP + clientes (real-time) |
| GET | /deep-data | Datos SNMP deep de APs |
| POST | /trigger-deep-discover | Disparar deep discovery APs |
| GET | /deep-discover-status | Progreso del job |
| GET | /available-metrics/{profile_id} | Metricas disponibles para AP |
| GET | /dashboard-charts | Series temporales agregadas |
| GET | /dashboard-rankings | Top-5 peor latencia/packet loss |
| GET/POST/DELETE | /dashboard-layout | CRUD layout dashboard |
| GET/PUT | /dashboard-notes | CRUD notas dashboard |
| GET | /report/{profile_id} | Informe AP individual |
| GET | /fleet-report | Informe flota completa |
| GET | /group-summary/{group_id} | Stats por grupo wireless |
| GET | /group-metric/{group_id} | Series temporales por grupo |
| PUT | /group-assign | Asignar APs a grupo |
UPS Monitor (/api/monitoring/ups/) — 18 endpoints
Estructura identica a Wireless Monitor. Sustituir “AP” por “UPS”, metricas de carga/bateria.
| Metodo | Endpoint | Proposito |
|---|---|---|
| GET | /summary | Stats agregados UPS (charge, load, critical) |
| GET | /status-list | Estado por UPS (real-time) |
| GET/POST | /deep-data, /trigger-deep-discover | Deep discovery UPS |
| GET | /dashboard-charts, /dashboard-rankings | Graficas + top-5 |
| GET/POST/DELETE | /dashboard-layout | Layout persistido |
| GET/PUT | /dashboard-notes | Notas persistidas |
| GET | /report/{profile_id}, /fleet-report | Informes individual/flota |
| GET | /group-summary/{id}, /group-metric/{id} | Stats/series por grupo |
| PUT | /group-assign | Asignar UPS a grupo |
Signage Monitor (/api/monitoring/signage/) — 23 endpoints
Misma estructura que Wireless/UPS + endpoints de deploy y operaciones.
Endpoints base (18): summary, status-list, deep-data, trigger-deep-discover, charts, rankings, layout, notes, reports, groups (identicos a Wireless).
Endpoints adicionales:
| Metodo | Endpoint | Proposito |
|---|---|---|
| POST | /deploy-content | Deploy media a dispositivo via Agent WebDAV |
| GET | /media-download/{asset_id} | Servir media para descarga Agent |
| POST | /publish-setup | Crear/obtener SignagePlayer + URL publish |
| GET | /operations | Listar operaciones recientes |
| POST | /operations/{id}/restore | Restaurar player a estado anterior |
Observatory Layout y Sentinel Config — 9 endpoints
| Metodo | Endpoint | Proposito |
|---|---|---|
| GET/POST/DELETE | /overview-layout | Layout persistido del overview |
| GET/PUT | /overview-notes | Quick notes |
| GET/PUT | /chart-defaults | Defaults de graficas |
| GET/PUT | /sentinel-config | Configuracion Sentinel Mode |
Network Tutor (/api/monitoring/sentinel/tutor/) — 3 endpoints
| Metodo | Endpoint | Proposito |
|---|---|---|
| POST | /ask | Pregunta de networking al tutor IA (async) |
| GET | /history | Historial de conversacion |
| POST | /clear | Limpiar historial |
Convenciones y restricciones
- ECharts local en
static/js/vendor/echarts.min.js— no cargar desde CDN (CSP) DeviceFilterServicecentraliza el filtrado del sidebar — no duplicar lógica de filtrado- Persistencia de pestañas abiertas: localStorage key
observatory_tabs - Zoom state por gráfica: localStorage key
observatory_zoom_{chartKey} - Shine (brillo global): localStorage key
crearack_shine - Auto-refresh HTTP: solo para pestañas HTTP abiertas (evita timeouts en dispositivos inactivos)
MonitoringTarget.get_or_create_for_device(device, org)para sincronización automática Device → Target
Véase también
- [[crearack-tech—backend—network-observatory]] — backend del Network Observatory
- [[crearack—monitoring—que-es-observatory]] — introducción al Observatory
- [[crearack—monitoring—metricas]] — métricas disponibles en Observatory
- [[crearack—monitoring—dashboards]] — dashboards de Observatory
- [[crearack—monitoring—configurar-snmp]] — configuración de SNMP
- [[crearack-tech—architecture—realtime-monitoring-plan]] — plan de monitoring en tiempo real
- [[entity—monitoring—model—monitoringtarget]] — target de polling en Observatory
- [[entity—monitoring—model—slapolicy]] — política de SLA para alertas