Entidaddraftcreado Mon Aug 31#terminal#monitoring#observability#reliability#django#postgres#multi-tenancy#audit
Propósito
AgentRoleEvent (terminal/models.py) es el rastro persistente de cada cambio de rol Primary/Secondary en la flota de Agentes de una organización. Antes de este modelo (task #234, v1.88.0) un cambio de Primary solo dejaba un logger.info que nadie consultaba; ahora cada transición real de rol (from_role != to_role) queda como una fila consultable desde el Fleet Manager.
Contrato
Campos principales (migración terminal/0007_agentroleevent.py + RLS gemela 0008_agentroleevent_rls.py, mismo patrón que network/0063):
| Campo | Tipo | Notas |
|---|---|---|
organization | FK → Organization, on_delete=CASCADE | ancla RLS multi-tenant, related_name="agent_role_events" |
agent_id | CharField(64), db_index=True | plano, no FK — sobrevive a la purga del AgentInstance (fantasmas offline >24h) |
hostname | CharField(255), opcional | idem, plano |
from_role / to_role | CharField(16) | from_role vacío = asignación inicial de un Agente recién instalado |
cause | CharField, choices manual / failover / auto_assign | |
actor | FK → User, SET_NULL, null=True | None = lo hizo el sistema (failover o asignación automática) |
from_agent_id / from_hostname | CharField, opcional | el Primary relevado, cuando la transición releva a otro |
created_at | DateTimeField(auto_now_add=True) |
Meta.ordering = ["-created_at"]; índice compuesto (organization, -created_at).
Dependencias
- Lo escribe
terminal.fleet_events.record_role_change()— única puerta de entrada, llamada desde cuatro sitios:terminal/api/fleet.py::promote_agent/demote_agent(causamanual),terminal/fleet_lifecycle.py::_handle_disconnect_failover(causafailover) y_register_and_assign_role(causafailoverpara el barrido de Primaries caducados,auto_assignpara el registro de un Agente nuevo). record_role_changees best-effort: un fallo al guardar se captura conlogger.exceptiony devuelveNone, sin romper la promoción o el failover que lo llamó. Sifrom_role == to_role(no hubo transición real) tampoco escribe nada.- Cuando la transición deja
to_role="primary"y había unfrom_roleprevio, dispara ademásnotify_primary_change()(mismo módulo) — correo best-effort a los admins de la organización. - Lo lee el endpoint
GET /api/agent/fleet/history(terminal/api/fleet.py::fleet_role_history, permisofleet:view, máx 200 filas) y la sección Role changes del Fleet Manager en el frontend.
Ejemplo de uso
from terminal.fleet_events import record_role_change
record_role_change(
organization_id=org.id,
agent_id=agent.agent_id,
hostname=agent.hostname,
from_role="secondary",
to_role="primary",
cause="manual",
actor=request.user,
from_agent_id=previous_primary.agent_id,
from_hostname=previous_primary.hostname,
)
Véase también
- [[entity—terminal—model—agentinstance]]
- [[entity—terminal—endpoint—fleet]]
- [[feature—terminal—fleet-role-history]]
- [[concept—saas—multi-tenancy]]
- [[entity—core—model—systemlog]]
- [[crearack—monitoring—fleet-manager]]