CreaRack-SL

Endpoint: POST /api/racks/integrity/ack — Registrar veredicto de reconciliación

Endpoint: POST /api/racks/integrity/ack — Registrar veredicto de reconciliación\n\nMódulo: racks.api.integrity.ack_finding() \nMétodo: POST \nRuta: /api/racks/integrity/ack \nAutenticación: Requerida \nParámetros: JSON body\n\n## Propósito\n\nRegistra un veredicto humano en la cola de reconciliación de Integridad:\n- “Plan updated” — usuario corrigió el plano.\n- “Real change” — usuario confirma cambio real en la red (abre incidencia ITSM si hay target).\n- “False alarm” — sensor erró (silencia 30 días).\n\nNuevo en F3: Respuesta mejorada con campo itsm que indica si se abrió incidencia CNS/ITSM.\n\n## Request\n\nSchema (AckIn):\npython\nclass AckIn(Schema):\n kind: str # \"matched_stale\", \"undocumented\", \"identity_conflict\"\n verdict: str # \"plan_updated\", \"real_change\", \"false_alarm\"\n device_id: int | None = None\n profile_id: int | None = None\n note: str = \"\"\n\n\nEjemplo:\nbash\ncurl -X POST http://localhost:8000/api/racks/integrity/ack \\\n -H \"Content-Type: application/json\" \\\n -H \"Authorization: Bearer <token>\" \\\n -d '{\n \"kind\": \"matched_stale\",\n \"verdict\": \"real_change\",\n \"device_id\": 42,\n \"note\": \"Equipment was physically removed last week\"\n }'\n\n\n## Response\n\nStatus: 200 OK \nContent-Type: application/json\n\n### Estructura\n\njson\n{\n \"ok\": true,\n \"ack_id\": 123,\n \"muted_until\": \"2026-08-17T17:40:28Z\",\n \"gate\": {\n \"fidelity\": 94.5,\n \"coverage\": 87.2,\n \"snapshots\": [\n {\"snapshot_date\": \"2026-07-17\", \"fidelity\": 94.5}\n ]\n },\n \"itsm\": {\n \"queued\": true\n }\n}\n\n\no (sujeto sin target de monitorización):\n\njson\n{\n \"ok\": true,\n \"ack_id\": 124,\n \"muted_until\": \"2026-08-17T17:40:28Z\",\n \"gate\": {...},\n \"itsm\": {\n \"queued\": false,\n \"reason\": \"subject has no monitoring target\"\n }\n}\n\n\no (veredicto no “real_change”):\n\njson\n{\n \"ok\": true,\n \"ack_id\": 125,\n \"muted_until\": \"2026-08-17T17:40:28Z\",\n \"gate\": {...},\n \"itsm\": null\n}\n\n\n### Campos de respuesta\n\n| Campo | Tipo | Descripción |\n|-------|------|-------------|\n| ok | bool | Siempre true si HTTP 200. |\n| ack_id | int | ID del IntegrityAck creado. |\n| muted_until | ISO8601 | Hasta cuándo se silencia el aviso:
- plan_updated: +30 días
- real_change: +30 días
- false_alarm: +30 días |\n| gate | object | Métricas de integridad (fidelidad, cobertura, histórico diario). |\n| itsm | object \| null | Nuevo en F3:
- {\"queued\": true} si veredicto es “real_change” Y sujeto tiene target.
- {\"queued\": false, \"reason\": \"....\"} si “real_change” pero sin target.
- null para otros veredictos. |\n\n## Flujo interno (F3)\n\n### 1. Crear IntegrityAck\n\npython\nack = IntegrityAck.objects.create(\n organization=org,\n device=device,\n profile=profile,\n kind=data.kind,\n verdict=data.verdict,\n subject_label=...,\n muted_until=timezone.now() + timedelta(days=integrity.ACK_MUTE_DAYS[data.verdict]),\n created_by=request.user,\n)\n\n\nSin incidencia aún (insight=None).\n\n### 2. Si veredicto es “real_change”: Validar target y encolar ticket\n\npython\nitsm: dict | None = None\nif data.verdict == integrity.VERDICT_REAL_CHANGE:\n from racks.services.integrity_itsm import resolve_ticket_target\n from racks.tasks import open_integrity_drift_ticket\n\n if resolve_ticket_target(org, device=device, profile=profile):\n # Target existe → encola creación de incidencia en Huey\n open_integrity_drift_ticket(ack.id)\n itsm = {\"queued\": True}\n else:\n # Sin target → honestidad total\n itsm = {\"queued\": False, \"reason\": \"subject has no monitoring target\"}\n\n\n### 3. Obtener métricas de integridad actuales\n\npython\ngate = integrity.gate_stats(org)\n\n\n### 4. Responder\n\npython\nreturn 200, {\n \"ok\": True,\n \"ack_id\": ack.id,\n \"muted_until\": ack.muted_until.isoformat(),\n \"gate\": gate,\n \"itsm\": itsm,\n}\n\n\n## Comportamiento por veredicto\n\n### “plan_updated” (Plan actualizado)\n\n- Significado: Usuario actualizó el plano para que coincida.\n- Mute: +30 días.\n- ITSM: No se abre incidencia (itsm=None).\n- Recheck: Próximo probe re-verifica sin esperar caducidad.\n\n### “real_change” (Cambio real)\n\n- Significado: Cambio confirmado en la red (equipo retirado, movido, sustituido).\n- Mute: +30 días.\n- ITSM: Abre incidencia AIInsight si target existe.\n - Background task (open_integrity_drift_ticket): Recomputa contexto + llamada IA.\n - Sin comandos: Nunca muestra botón Apply (is_actionable=False).\n - TTL: 14 días.\n - Reconciliación: Si usuario acknowledge en Observatory, el ack se des-silencia para re-check.\n- Response: {\"queued\": true} o {\"queued\": false, \"reason\": \"subject has no monitoring target\"}.\n\n### “false_alarm” (Falsa alarma)\n\n- Significado: Sensor se equivocó (ruido).\n- Mute: +30 días.\n- ITSM: No se abre incidencia (itsm=None).\n- Recheck: Próximo probe re-verifica sin esperar caducidad.\n\n## Errores posibles\n\n| HTTP | Razón | Ejemplo |\n|------|-------|----------|\n| 400 | Body inválido (falta kind, verdict, etc.) | {\"detail\": \"Invalid schema\"} |\n| 401 | No autenticado | Request sin token. |\n| 403 | RLS: org no pertenece al usuario. | Usuario intenta crear ack en org ajena. |\n| 404 | Device/profile no existe. | device_id=999 no encontrado. |\n| 500 | Error interno (rate limit, proveedor caído, etc.). | Falla en open_integrity_drift_ticket. |\n\nNota: open_integrity_drift_ticket es best-effort en background. Si falla, el ack ya está creado (ok=true) pero el ticket no se abre; se loguea como aviso.\n\n## Testing\n\nCubierto por 3 tests:\n- ✅ Veredicto “real_change” con target → itsm.queued=true.\n- ✅ Veredicto “real_change” sin target → itsm.queued=false, reason=\"...\" .\n- ✅ Veredicto “false_alarm” → itsm=null.\n\n## Integraciones\n\n### Frontend: static/js/pages/integrity.js\n\njavascript\nasync function sendVerdict(btn) {\n const body = {\n kind: btn.dataset.kind,\n verdict: btn.dataset.verdict,\n device_id: btn.dataset.deviceId || null,\n profile_id: btn.dataset.profileId || null,\n };\n const res = await fetch(\"/api/racks/integrity/ack\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const out = await res.json();\n showItsmNote(out.itsm); // Muestra respuesta ITSM 10s\n await load(); // Repinta cola sin este aviso\n}\n\nfunction showItsmNote(itsm) {\n if (!itsm) return;\n const el = document.getElementById(\"integrity-itsm-note\");\n el.textContent = itsm.queued\n ? t9(\"Real change recorded — an ITSM incident is being opened...\")\n : t9(\"Real change recorded — no ITSM incident: this subject has no monitoring target.\");\n el.hidden = false;\n clearTimeout(itsmNoteTimer);\n itsmNoteTimer = setTimeout(() => { el.hidden = true; }, 10000);\n}\n\n\n

Véase también

  • [[feature—racks—barra-integridad-f3]]
  • [[entity—racks—model—integrity-ack]]
  • [[entity—racks—service—integrity-itsm]]