Descripción
Función: reopen_group(group: IncidentGroup) -> None
Ubicación: monitoring/services/correlation_service.py (líneas ~126–133)
Cuando un nuevo insight se correlaciona a un grupo que ya estaba resuelto, este servicio reabre el grupo automáticamente limpiando resolved_at.
Se invoca desde correlate_new_insight() cuando la lógica de correlación asigna un insight a un grupo existente que ya está cerrado.
Firma y comportamiento
def reopen_group(group):
"""Reopen a resolved group when a new insight correlates into it."""
if group.status == "resolved":
group.status = "open"
group.resolved_at = None
group.save(update_fields=["status", "resolved_at"])
logger.info(f"Reopened incident group '{group.title}' (new correlated insight)")
Semántica
Un grupo resuelto significa “no queda nada accionable” en ese momento. Si aparece un nuevo insight correlacionado (misma red, mismo target, mismo patrón), la situación cambió: hay trabajo nuevo.
- Antes: Grupo “resuelto” desde el 05-08, MTTR capturado.
- Ahora: Nuevo insight aparece el 10-08, correlaciona al grupo viejo.
- Acción: Grupo se reabre (
status = "open",resolved_at = NULL). - Efecto: El grupo vuelve a la pestaña ITSM de grupos abiertos, invita a revisión.
Integración en correlate_new_insight()
La función de correlación intenta 3 reglas (en orden):
- Regla 1: Mismo target → busca otro insight en el mismo target sin grupo.
- Regla 2: Misma subnet → busca otro insight en misma subnet sin grupo.
- Regla 3: Fingerprint → busca patrón existente por anomalía/host.
En cada regla, si encuentra un grupo existente:
def correlate_new_insight(insight):
# Regla 1
same_target = AIInsight.objects.filter(target=insight.target, status__in=RECENT_STATUSES).first()
if same_target and same_target.incident_group:
insight.incident_group = same_target.incident_group
insight.save(update_fields=["incident_group"])
reopen_group(same_target.incident_group) # ← AQUÍ
logger.info(f"Correlated {insight.case_id} → group '{same_target.incident_group.title}' (rule 1)")
return same_target.incident_group
# Regla 2
same_subnet = ...
if same_subnet and same_subnet.incident_group:
insight.incident_group = same_subnet.incident_group
insight.save(update_fields=["incident_group"])
reopen_group(same_subnet.incident_group) # ← AQUÍ
logger.info(f"Correlated {insight.case_id} → group '{same_subnet.incident_group.title}' (rule 2)")
return same_subnet.incident_group
Caso de uso real
Escenario: Switch S1 entra en bucle STP a las 03:00, se detecta.
- 03:15 → Insight A: “STP loop on S1”. Grupo “Switch-Storm-S1” creado.
- 04:30 → Reconoces y cierras el insight (ACK). Grupo se resuelve.
- 10:00 → Switch S1 vuelve a entrar en loop (problema no arreglado en realidad, falsa recuperación). Insight B detecta.
- 10:02 → Correlación: Insight B busca insights recientes en S1 → encuentra A (en grupo).
- Acción: Grupo “Switch-Storm-S1” se reabre. MTTR del primer episodio se captura; MTTR del segundo episodio nuevo empieza.
Sin esto, Insight B se perdería en el grupo ya cerrado y no sería accionable desde la UI.
Estados posibles
open ─→ (close_group_if_done) ─→ resolved
↑ │
└────── (reopen_group) ────────────┘
Flujo normal:
- Grupo creado en
open. - Al cerrar todos los insights, pasa a
resolved(con fecha). - Si insight nuevo correlaciona, regresa a
open(sin fecha).
Precondiciones y garantías
Precondiciones:
groupdebe ser objetoIncidentGroupválido (no None).- Solo actúa si
group.status == "resolved"(noop si ya está abierto).
Garantías:
- Idempotente: llamar múltiples veces sobre el mismo grupo no daña (el segundo
reopenes noop). - Atómico: estado y fecha se actualizan en una transacción.
- Seguro: si
group.statusya esopen, no hace nada.
Interacción con close_group_if_done()
Estas dos funciones forman un ciclo de vida simétrico:
| Transición | Función | Condición |
|---|---|---|
open → resolved | close_group_if_done() | Todos los insights en estado terminal (ack/applied/expired). |
resolved → open | reopen_group() | Nuevo insight correlaciona al grupo. |
Invariante: Un grupo nunca salta open ↔ resolved sin razón. El cambio siempre tiene causa en los datos de insights.
Pruebas
Véase tests/api/test_itsm_close_cycle.py:
- ✅
test_reopen_group— grupo resuelto se reabre correctamente. - ✅
test_group_closes_when_last_insight_acked— cierre y reapertura simetría (implícito).
Impacto en UX
- Pestaña ITSM: Grupos reabiertos aparecen en “Abiertos” aunque antes estaban “Resueltos”.
- Histórico: Las fechas de cierre anteriores se preservan (MTTR del episodio anterior capturado).
- Alertas: Si hay suscriptores a grupos, reapertura puede gatillar notificación.
Véase también
- [[feature—monitoring—itsm-ciclo-cierre]]
- [[decision—20260814—itsm-closed-at-semantics]]
- [[entity—monitoring—service—close-group-if-done]]
- [[entity—monitoring—model—incident-group]]