CreaRack-SL

Descripción

Modelo Django que almacena entradas de auditoría del sistema: quién hizo qué, cuándo, en qué organización y con qué detalles. Trazabilidad completa de mutaciones sensibles.

Tabla: core_systemlog
Ubicación: core.models.SystemLog
Disponible desde: v1.0.73 (con categorías ITSM y OBSERVATORY desde PR #71; CREDENTIAL desde v1.103.0 / commit d14ebb58)

Campos

CampoTipoNuleableDescripción
idAutoFieldNoClave primaria
timestampDateTimeFieldNoInstante del evento (siempre timezone.now())
userForeignKey(User)SíOperador que realizó la acción. None si anónimo o token sin identidad
organizationForeignKey(Organization)SíOrg afectada. None si a nivel de sistema (raro)
levelCharField(max_length=10)NoSeveridad: INFO, WARN, ERROR
categoryCharField(max_length=50)NoCategoría de la acción (ver tabla abajo)
actionCharField(max_length=200)NoIdentificador de la acción (ej: channel.create, alert.delete)
detailsTextFieldSíPayload libre — JSON, texto plano o URL-encoded (sin restricción de formato)

Categorías (CATEGORY_CHOICES)

ClaveEtiquetaUsoIntroducido
AUTHAuthenticationLogins, logouts, cambios de contraseñav1.0.0
RACKRack ManagementCRUD de racks, cambios de estadov1.0.0
BLUEPRINTBlueprint ManagementCambios en blueprintsv1.0.0
SYSTEMSystemJobs, rebalances, maintenance internov1.0.0
NETWORKNetwork ManagementCambios en topología de red (CNS)v1.0.0
ITSMITSM ManagementCanales, escalados, SLA, mantenimiento, runbooksPR #71 (2026-06-07)
OBSERVATORYObservatory ManagementAlertas, wireless, UPS, descubrimientosPR #71 (2026-06-07)
CREDENTIALCredential StoreAlta/edición/borrado/desencriptado de credenciales guardadasv1.103.0 / commit d14ebb58 (2026-09-03)

Acciones por categoría (no exhaustivo)

ITSM:

  • sla.update — Cambio de política de SLA
  • channel.create, channel.update, channel.delete — Gestión de canales de notificación
  • escalation.create, escalation.update, escalation.delete — Políticas de escalado
  • maintenance.create, maintenance.update, maintenance.delete — Ventanas de mantenimiento
  • runbook.create, runbook.delete, runbook.apply — Runbooks

OBSERVATORY:

  • alert.delete, alert.resolve, alert.threshold — Alertas
  • wireless.deep_discover, wireless.group_assign — APs y wireless controllers
  • ups.deep_discover, ups.group_assign — UPS

CREDENTIAL:

  • credential.create, credential.update, credential.delete — Alta, edición y baja de una credencial guardada
  • credential.decrypt — Acceso al secreto en claro (p.ej. para conectar con un dispositivo)
  • El valor de la credencial NUNCA viaja en details — solo metadata (qué credencial, quién, cuándo)

Índices

CREATE TABLE core_systemlog (
    id BIGINT PRIMARY KEY,
    timestamp TIMESTAMP NOT NULL,
    user_id BIGINT NULL REFERENCES core_user(id),
    organization_id BIGINT NULL REFERENCES core_organization(id),
    level VARCHAR(10) NOT NULL,
    category VARCHAR(50) NOT NULL,
    action VARCHAR(200) NOT NULL,
    details TEXT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_systemlog_org_category_action_ts 
  ON core_systemlog(organization_id, category, action, timestamp DESC);

CREATE INDEX idx_systemlog_org_user_ts 
  ON core_systemlog(organization_id, user_id, timestamp DESC);

CREATE INDEX idx_systemlog_level 
  ON core_systemlog(level, timestamp DESC);

Relaciones

  • ForeignKey → User: El operador (anónimo si user_id = NULL)
  • ForeignKey → Organization: La org afectada (respeta RLS)
  • Sin FK a otras entidades: Los detalles se almacenan en texto (details) para flexibilidad

Métodos y propiedades

__str__()

def __str__(self) -> str:
    """Retorna representación humana: '<2026-06-07 10:55 user=operator org=acme action=channel.create>'"""

Migración reciente: 0034

Cambio: Extensión de CATEGORY_CHOICES con CREDENTIAL (cola de auditoría, task #286).

# core/migrations/0034_systemlog_category_credential.py
# Generated: 2026-09-03

migrations.AlterField(
    model_name='systemlog',
    name='category',
    field=models.CharField(
        choices=[
            ('AUTH', 'Authentication'),
            ('RACK', 'Rack Management'),
            ('BLUEPRINT', 'Blueprint Management'),
            ('SYSTEM', 'System'),
            ('NETWORK', 'Network Management'),
            ('ITSM', 'ITSM Management'),
            ('OBSERVATORY', 'Observatory Management'),
            ('CREDENTIAL', 'Credential Store'),          # NEW
        ],
        max_length=50,
    ),
)

Motivo: core/credential_api.py gestionaba credenciales (crear/editar/borrar/desencriptar) sin dejar rastro en SystemLog — un operador podía leer el secreto de un dispositivo sin que quedara registro alguno. La cola de auditoría (task #286, 14 hallazgos MEDIA) lo detectó y añadió el rastro completo, sin loguear nunca el valor del secreto.

Impacto: No-op para datos existentes. Las nuevas mutaciones de credenciales quedan auditadas.

Migración anterior: 0023

Cambio: Extensión de CATEGORY_CHOICES con ITSM y OBSERVATORY.

# core/migrations/0023_alter_systemlog_category.py
# Generated: 2026-06-07

migrations.AlterField(
    model_name='systemlog',
    name='category',
    field=models.CharField(
        choices=[
            ('AUTH', 'Authentication'),
            ('RACK', 'Rack Management'),
            ('BLUEPRINT', 'Blueprint Management'),
            ('SYSTEM', 'System'),
            ('NETWORK', 'Network Management'),
            ('ITSM', 'ITSM Management'),           # NEW
            ('OBSERVATORY', 'Observatory Management'),  # NEW
        ],
        max_length=50,
    ),
)

Impacto: No-op para datos existentes. Las nuevas mutaciones pueden usar las nuevas categorías.

Lectura de auditoría

API: GET /api/core/logs/ (si existe)

Típicamente expuesto via core/api/logs.py → SystemLogSchema (readonly):

class SystemLogSchema(Schema):
    id: int
    timestamp: datetime
    level: str
    category: str
    action: str
    details: str
    user_id: Optional[int]
    username: str  # derived

Django admin

Registrado en core/admin.py para navegación manual (solo superusers).

Queries comunes

# Auditoría de un operador en una org
SystemLog.objects.filter(
    organization=org,
    user=operator,
    timestamp__gte=start_date
).order_by('-timestamp')

# Todas las mutaciones de un tipo
SystemLog.objects.filter(
    organization=org,
    category='ITSM',
    action='channel.delete'
)

# Logs de error
SystemLog.objects.filter(level='ERROR')

Retención y archivado

  • Estrategia actual: Indefinida (considerar política de archivado anual)
  • Tamaño estimado: ~200-300 bytes por entrada; ~1-2 MB/mes para org activa
  • TTL recomendado: 7 años (SOC2/ISO27001 compliance)

Integración con observabilidad

Las entradas de SystemLog pueden ingerirse en Prometheus/Grafana vía:

  1. Endpoint expuesto (lectura filtrada por org)
  2. Exporter custom (convierte rows a métricas)
  3. ELK/Splunk (dump periódico a datawarehouse)

RLS

  • organization siempre se valida mediante get_current_org(request) antes de escribir
  • Un operador no puede auditar ni ver logs de orgs ajenas
  • Si RLS falla en log_action(), la excepción es capturada → organization=None
  • Código que corre FUERA de un request (tareas Huey) no fija el GUC de RLS por sí solo — necesita core.utils.rls.rls_bypass(), ver [[incident—20260903—huey-tasks-sin-rls-bypass]]

Performance

  • Escritura: Operación rápida (una INSERT); best-effort en log_action() (nunca bloquea request)
  • Lectura: Índice en (organization, category, timestamp DESC) → <100ms para 1M rows
  • Vacuum: Índices sin fragmentación si hay INSERT masivos

Cambios futuros

  • Compresión de details si campo crece (JSONB con índices)
  • Particionado temporal (monthly tables) si >100M rows/año
  • Exportación a S3/Datadog para compliance reporting

Véase también

  • [[entity—core—service—log-action]] — Helper que escribe en este modelo
  • [[feature—monitoring—audit-logs-mutaciones-sa4-sa5]] — Feature que añadió categorías ITSM/OBSERVATORY
  • [[decision—20260607—audit-logs-best-effort]] — ADR sobre la estrategia best-effort