Volver a la wiki

Servicio monitoring · provisioning — auto-provisionamiento de objetivos SNMP

Descripción

Módulo centralizado de auto-provisionamiento del Observatory: encapsula la lógica de descubrir DeviceProfile sin MonitoringTarget asociado y crear los objetivos correspondientes (idempotente mediante bulk_create(ignore_conflicts=True)).

Antes: la lógica estaba duplicada (~110 LOC casi idéntica) en las 4 vistas HTMX del Observatory (wireless_view, observatory_view, ups_view, signage_view). Ahora vive aquí, permitiendo que el caller gate la escritura detrás de observatory:edit.

Archivo: monitoring/services/provisioning.py (89 LOC públicas)
Introducido en: commit f263a03 (sesión s108, auditoría sa6)

API Pública

provision_profile_targets(org, profiles, scope) → None

Auto-provisiona un MonitoringTarget para cada entrada en profiles que:

Parámetros:

Comportamiento:

  1. Busca objetivos existentes en la org + scope con un único filter(organization=org, scope=scope).
  2. Itera los perfiles: para cada uno sin linked_monitoring_target, crea un nuevo MonitoringTarget con:
    • ip_address del perfil.
    • name = hostname del perfil o IP en fallback.
    • ping_enabled=True.
    • snmp_enabled = verdadero solo si el perfil tiene credenciales SNMP.
    • http_enabled=False.
    • enabled=True.
    • config = dict SNMP compilado por build_snmp_config(profile).
    • device = device vinculado del perfil (si existe).
  3. Persiste con bulk_create(new_targets, ignore_conflicts=True) — idempotente: si el objetivo ya existe (por una carrera concurrente), se ignora sin error.
  4. Re-busca objetivos recién creados y vincula sus PKs (asignado por la BD) a los perfiles.
  5. Actualiza todos los perfiles con bulk_update(profiles_to_link, ["linked_monitoring_target"]).

Fuente de datos:

Restricciones de seguridad:

Idempotencia: sí. Múltiples llamadas sucesivas con los mismos perfiles y scope no crean duplicados; perfil con linked_monitoring_target ya seteado se salta.

build_snmp_config(profile) → dict

Arma el dict de configuración SNMP desde las credenciales de un DeviceProfile.

Parámetros:

Devuelve: dict con claves:

Propósito: centralizar la compilación de config para evitar duplication entre vistas y hacer más fácil futuros cambios (p.ej., secretos cifrados, validación de claves).

Helpers privados

Decisiones de diseño

  1. Idempotencia mediante bulk_create(..., ignore_conflicts=True): si dos requests concurrentes detectan el mismo perfil sin objetivo, ambos crean, pero solo uno persiste (basado en índice único (organization, ip_address, scope)). El otro se ignora sin error.

  2. Escrituras en dos queries: primero bulk_create, luego re-scan y bulk_update. Esto es más eficiente que un loop de save() por perfil (lo que ocurría antes en las vistas).

  3. Vinculación de PKs post-bulk: tras bulk_create(..., ignore_conflicts=True), los PKs de nuevos objetivos no están siempre asignados (depende del backend). Se re-buscan con filter(organization=org, scope=scope, ip_address__in=created_ips) y se vinculan a los perfiles.

  4. Caller es responsable del gate de permisos: este servicio NO valida observatory:edit. Las vistas lo validan antes de llamar. Esto mantiene el módulo enfocado en la lógica de provisión.

Contexto: Broken Access Control auditado (sa6)

Antes de este cambio, cada una de las 4 vistas hacía if user.is_authenticated: (sin gate de rol) y luego llamaba a la lógica de provisión inline. Un usuario readonly podía provocar escrituras abriendo la página.

Ahora: la lógica de provisión está aquí, y el caller (vistas) valida has_permission(user, "observatory", "edit") antes de llamar. El modelo refuerza la garantía: la escritura no puede ocurrir sin permiso.

Véase también

Subir