Arquitectura de Base de Datos - CreaRack Pro v1.0.50
Última actualización: 03-04-2026 Versión de la aplicación: v1.0.50 Motor: PostgreSQL (16 en producción, 17 en desarrollo) ORM: Django 6.0.3 Total de modelos: 42 Total de migraciones: 112
1. Visión General
CreaRack Pro utiliza PostgreSQL como base de datos relacional principal, gestionada a través del ORM de Django 6. La aplicación está organizada en 7 apps Django, cada una con sus propios modelos y migraciones.
Distribución de Modelos por App
| App | Modelos | Migraciones | Descripción |
|---|---|---|---|
core | 10 | 16 | Usuarios, organizaciones, permisos, credenciales, logs |
racks | 6 | 10 | Racks, dispositivos, stencils, backups |
blueprints | 4 | 6 | Mapas, planos, anotaciones, prompts AI |
network | 4 | 44 | Perfiles de red, vendors, MIBs, conexiones de puerto |
monitoring | 16 | 17 | Monitoreo, alertas, métricas, CNS, ITSM |
signage | 11 | 8 | Digital Signage CMS, playlists, schedules, portal |
terminal | 2 | 2 | Scripts de terminal, instancias de agente |
Configuración de Conexión
Desarrollo (config/settings/dev.py)
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "crearack_pro",
"USER": "crearack_user",
"PASSWORD": "dev_password_123",
"HOST": os.getenv("POSTGRES_HOST", "localhost"), # 'db' en Docker
"PORT": "5432",
"OPTIONS": {"connect_timeout": 10},
# Sin CONN_MAX_AGE (nueva conexión por request)
}
}
Producción (config/settings/production.py)
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.getenv("POSTGRES_DB", "crearack_pro"),
"USER": os.getenv("POSTGRES_USER", "crearack_user"),
"PASSWORD": os.getenv("POSTGRES_PASSWORD"),
"HOST": os.getenv("POSTGRES_HOST", "db"),
"PORT": "5432",
"CONN_MAX_AGE": 600, # Reutilizar conexiones 10 min
"CONN_HEALTH_CHECKS": True, # Django 6: verificar conexión antes de reutilizar
"OPTIONS": {"connect_timeout": 10},
}
}
2. Configuración Docker
Desarrollo (compose.yml)
db:
image: postgres:18-alpine
container_name: crearack_db
environment:
POSTGRES_DB: crearack_pro
POSTGRES_USER: crearack_user
POSTGRES_PASSWORD: dev_password_123
volumes:
- pg_data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U crearack_user -d crearack_pro"]
interval: 5s
timeout: 5s
retries: 5
- Imagen:
postgres:18-alpine - max_connections: Por defecto PostgreSQL (100)
- Volumen:
pg_data(Docker named volume) - Red:
crearack_internal(bridge)
Producción (compose.prod.yml)
db:
image: postgres:18-alpine
restart: unless-stopped
command: >-
postgres
-c max_connections=200
-c shared_buffers=256MB
-c shared_preload_libraries=pg_stat_statements
-c pg_stat_statements.track=all
environment:
POSTGRES_DB: ${POSTGRES_DB:-crearack_pro}
POSTGRES_USER: ${POSTGRES_USER:-crearack_user}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- pg_data_prod:/var/lib/postgresql/data
pgbouncer:
image: edoburu/pgbouncer:latest
environment:
DATABASE_URL: postgres://...@db:5432/crearack_pro
POOL_MODE: transaction
DEFAULT_POOL_SIZE: 30
MAX_CLIENT_CONN: 300
depends_on:
db: { condition: service_healthy }
- Imagen:
postgres:18-alpine(migrado desde 16 el 04-04-2026) - max_connections:
200(bajado desde 500 gracias a pgbouncer) - shared_buffers:
256MB - pg_stat_statements: Activado (slow query tracking)
- pgbouncer: Transaction pooling, pool=30, max_client=300
- CONN_MAX_AGE:
0en Django (obligatorio con pgbouncer transaction mode) - Volumen:
pg_data_prod(Docker named volume) - Red:
crearack_internal(bridge) - Contenedor en Hetzner:
crearack-pro-zcmvsl-db-1 - Monitoring: Huey task
monitor_db_connectionscada 5 min, alerta en SystemLog al 80% - Backup verification:
testzip()tras cada backup diario (3:30 AM)
3. Modelo Multi-Tenancy
CreaRack Pro implementa multi-tenancy con dos capas de aislamiento:
- Capa aplicación (Django ORM): Todos los queries filtran por
organization=org - Capa base de datos (PostgreSQL RLS): Políticas Row-Level Security como red de seguridad (v1.0.50)
Patrón de Aislamiento (ORM)
Organization (tenant raíz)
├── User (FK directa → organization)
├── Rack (FK directa → organization)
│ └── Device (FK indirecta vía rack.organization)
│ └── ConfigBackup (FK indirecta vía device.rack.organization)
├── Blueprint (FK directa → organization)
├── DeviceProfile (FK directa → organization)
├── MonitoringTarget (FK directa → organization)
├── AIInsight (FK directa → organization)
├── SignagePlayer (FK directa → organization)
├── AgentInstance (FK directa → organization)
├── Script (FK directa → organization)
├── StoredCredential (FK directa → organization)
├── SystemLog (FK directa → organization, nullable)
└── ScriptTemplate (FK directa → organization, nullable = global)
Modelos Globales (sin Organization FK)
| Modelo | Razón |
|---|---|
SaaSModule | Catálogo global de módulos |
Plan | Catálogo global de planes |
VendorProfile | Base de datos global de vendors de red |
SignageVendorAdapter | Adaptadores de vendor globales |
Stencil | Global si organization=NULL, tenant-specific si tiene FK |
Soft Delete
Los siguientes modelos implementan borrado lógico con campo deleted_at:
| Modelo | Campo | Comportamiento |
|---|---|---|
Organization | deleted_at (DateTimeField, nullable) | Reciclaje de organizaciones |
Rack | deleted_at (DateTimeField, nullable, db_index) | Papelera de racks |
Blueprint | deleted_at (DateTimeField, nullable, db_index) | Papelera de mapas |
Métodos: soft_delete(), restore(), propiedad is_deleted.
PostgreSQL Row-Level Security (v1.0.50)
Migration:
core/0017_rls_policies
RLS añade protección a nivel de base de datos. Si un bug en el ORM omite el filtro por organización, PostgreSQL bloquea el acceso.
Mecanismo: TenantRLSMiddleware ejecuta SET app.current_org_id por request. PostgreSQL aplica políticas USING (organization_id = current_setting('app.current_org_id')::int).
Cobertura: 35 tablas con políticas:
- 29 tablas strict (org NOT NULL): Solo filas de la organización del request
- 6 tablas nullable (Stencil, User, etc.): Filas propias + globales (
org IS NULL) - Bypass: Superusers (
org_id=0), requests sin auth
Tablas con FK heredada (Device, ConfigBackup, BlueprintPlacement, etc.) no necesitan política propia — la integridad referencial protege automáticamente.
Tenant Lifecycle
| Fase | Mecanismo | Timing |
|---|---|---|
| Onboarding | POST /api/signup — transaction atómica | On demand |
| Backup | run_org_backups Huey task — ZIP per-org | Diario 3:30 AM |
| Soft-delete | Organization.deleted_at → inaccesible | Inmediato |
| Purge | purge_deleted_organizations — hard-delete | Diario 4:00 AM (>90 días) |
| Integridad | check_tenant_integrity — anomalías cross-tenant | Diario 4:30 AM |
4. Referencia de Modelos por App
4.1 App core
4.1.1 SaaSModule
Tabla: core_saasmodule
Registro de módulos de aplicación disponibles para suscripción.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
slug | SlugField(50) | UNIQUE | Clave única del módulo |
name | CharField(100) | NOT NULL | Nombre visible |
description | TextField | blank | Descripción |
url_prefixes | JSONField | default=list | Prefijos URL gateados, ej: ["/editor/"] |
is_core | BooleanField | default=False | Si es core, siempre habilitado |
sort_order | IntegerField | default=0 | Orden de visualización |
Meta: ordering = ['sort_order', 'name']
4.1.2 Plan
Tabla: core_plan
Plan de suscripción que define módulos base para una organización.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
slug | SlugField(50) | UNIQUE | Clave única del plan |
name | CharField(100) | NOT NULL | Nombre del plan |
description | TextField | blank | Descripción |
sort_order | IntegerField | default=0 | Orden |
M2M: modules → SaaSModule (through: core_plan_modules, related_name: plans)
Meta: ordering = ['sort_order']
Choices para slug: starter, pro, custom
4.1.3 Organization
Tabla: core_organization
Tenant SaaS. Todas las entidades pertenecen a una organización.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
name | CharField(255) | NOT NULL | Nombre de la organización |
address | TextField | blank | Dirección |
created_at | DateTimeField | auto_now_add | Fecha de creación |
updated_at | DateTimeField | auto_now | Fecha de última actualización |
is_active | BooleanField | default=True | Estado activo |
max_racks | IntegerField | default=10 | Límite de racks |
plan_id | FK → Plan | SET_NULL, nullable | Plan de suscripción |
disable_reason | CharField(300) | blank, default=” | Razón de deshabilitación |
deleted_at | DateTimeField | nullable | Soft-delete timestamp |
logo_path | CharField(255) | blank, default=” | Ruta del logo |
session_timeout_minutes | IntegerField | default=10 | Timeout de sesión |
onboarding_completed | BooleanField | default=False | Onboarding completado |
sentinel_config | JSONField | default=dict | Config de Sentinel Mode |
backup_retention_days | IntegerField | default=0 | Días retención backups (0=default plan) |
M2M: extra_modules → SaaSModule (through: core_organization_extra_modules, related_name: extra_organizations)
4.1.4 User
Tabla: core_user
Modelo de usuario custom que extiende AbstractUser de Django. Definido como AUTH_USER_MODEL = "core.User".
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
| Campos heredados de AbstractUser | username, first_name, last_name, email, password, is_staff, is_active, is_superuser, date_joined, last_login | ||
organization_id | FK → Organization | CASCADE, nullable | Organización del usuario |
role | CharField(20) | choices, default=‘readonly’ | Rol: admin, operator, readonly |
Constraints:
UniqueConstraint(fields=['email'], condition=~Q(email=''), name='unique_email_when_set')— email único cuando no está vacío
4.1.5 SystemLog
Tabla: core_systemlog
Log de auditoría del sistema.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
timestamp | DateTimeField | auto_now_add, db_index | Fecha/hora |
level | CharField(20) | choices | INFO, WARNING, ERROR |
category | CharField(50) | choices | AUTH, RACK, BLUEPRINT, SYSTEM, NETWORK |
action | CharField(200) | NOT NULL | Acción registrada |
details | TextField | blank | Detalles |
user_id | FK → User | SET_NULL, nullable | Usuario que realizó la acción |
organization_id | FK → Organization | CASCADE, nullable | Organización |
Indexes:
(organization, -timestamp)(user, -timestamp)(level, -timestamp)
Meta: ordering = ['-timestamp']
4.1.6 ScriptTemplate
Tabla: core_scripttemplate
Plantillas de scripts de configuración de red reutilizables.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
name | CharField(200) | NOT NULL | Nombre |
description | TextField | blank | Descripción |
script_content | TextField | NOT NULL | Contenido del script |
language | CharField(50) | choices, default=‘cisco_ios’ | cisco_ios, junos, aruba, bash, python |
organization_id | FK → Organization | CASCADE, nullable | NULL = plantilla global |
created_by_id | FK → User | SET_NULL, nullable | Creador |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now |
Constraints: unique_together = ('name', 'organization')
4.1.7 ImpersonationLog
Tabla: core_impersonationlog
Log de auditoría para sesiones de impersonación de superusuario.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
admin_id | FK → User | CASCADE | Superusuario que impersona |
target_id | FK → User | CASCADE | Usuario impersonado |
started_at | DateTimeField | auto_now_add | Inicio de sesión |
ended_at | DateTimeField | nullable | Fin de sesión |
reason | CharField(200) | NOT NULL | Razón de la impersonación |
ip_address | GenericIPAddressField | nullable | IP del admin |
Meta: ordering = ['-started_at']
4.1.8 ModulePermission
Tabla: core_modulepermission
Permisos granulares por usuario (override sobre el rol).
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
user_id | OneToOneField → User | CASCADE, UNIQUE | Usuario |
permissions | JSONField | default=dict | {"scope": "level"}, ej: {"terminal": "none"} |
updated_by_id | FK → User | SET_NULL, nullable | Quién actualizó |
updated_at | DateTimeField | auto_now |
4.1.9 TemporaryAccess
Tabla: core_temporaryaccess
Elevación temporal de permisos con tiempo limitado.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
user_id | FK → User | CASCADE | Usuario beneficiario |
scope | CharField(20) | NOT NULL | all o scope específico |
level | CharField(10) | NOT NULL | view, edit, admin |
granted_by_id | FK → User | CASCADE | Quién otorgó |
reason | CharField(200) | NOT NULL | Razón |
granted_at | DateTimeField | auto_now_add | |
expires_at | DateTimeField | NOT NULL | Expiración |
revoked_at | DateTimeField | nullable | Revocación anticipada |
Meta: ordering = ['-granted_at']
4.1.10 StoredCredential
Tabla: core_storedcredential
Almacenamiento cifrado (Fernet AES) de credenciales SNMP/SSH/HTTP.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
name | CharField(200) | NOT NULL | Nombre descriptivo |
credential_type | CharField(10) | choices | snmp_v2, snmp_v3, ssh, multi |
encrypted_data | JSONField | default=dict | Datos cifrados con Fernet |
created_by_id | FK → User | SET_NULL, nullable | |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now |
Constraints: unique_together = ('name', 'organization')
Cifrado: Los campos sensibles (community, auth_key, priv_key, ssh_password, enable_password, http_password) se cifran con Fernet AES via CredentialManager. El método get_section(section_name) descifra bajo demanda.
4.1.11 LoginLog
Tabla: core_loginlog
Registro de cada intento de login (exitoso y fallido).
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
user_id | FK → User | CASCADE, nullable | NULL para logins fallidos |
timestamp | DateTimeField | auto_now_add, db_index | |
ip_address | GenericIPAddressField | nullable | IP del intento |
user_agent | CharField(512) | blank, default=” | User-Agent del navegador |
success | BooleanField | default=True | Login exitoso o fallido |
username_attempted | CharField(150) | blank, default=” | Username intentado |
method | CharField(20) | choices, default=‘password’ | password, passkey, social, token |
Indexes:
(user, -timestamp)(success, -timestamp)(ip_address, -timestamp)
Meta: ordering = ['-timestamp']
4.2 App racks
4.2.1 BoxCategory
Tabla: racks_boxcategory
Categorías para dispositivos.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
name | CharField(100) | NOT NULL | Nombre de la categoría |
organization_id | FK → Organization | CASCADE, nullable | NULL = categoría global |
Constraints: unique_together = ('name', 'organization')
4.2.2 RackGroup
Tabla: racks_rackgroup
Agrupación lógica de racks (ej: ‘Data Center A’, ‘Row 1’). También utilizado por DeviceProfile, SignagePlayer y Device como grupo genérico.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
name | CharField(100) | NOT NULL | Nombre del grupo |
color | CharField(20) | default=‘#6c757d’ | Color para la UI |
organization_id | FK → Organization | CASCADE | |
created_at | DateTimeField | auto_now_add |
Constraints: unique_together = ('name', 'organization')
4.2.3 Stencil
Tabla: racks_stencil
Plantillas de dispositivos (device templates) para el editor de racks.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
name | CharField(100) | NOT NULL | Nombre del stencil |
image_path | CharField(200) | NOT NULL | Ruta de la imagen |
default_u_height | IntegerField | default=1 | Altura en unidades de rack |
manufacturer | CharField(100) | blank | Fabricante |
category | CharField(100) | default=‘General’ | Categoría (legacy string) |
extra_data | TextField | blank, default=’{}’ | JSON con datos extra (half_width, etc.) |
organization_id | FK → Organization | CASCADE, nullable | NULL = stencil global |
4.2.4 Rack
Tabla: racks_rack
Rack/armario de servidor.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
name | CharField(100) | NOT NULL | Nombre |
location | CharField(100) | blank | Ubicación |
notes | TextField | blank | Notas |
height_u | IntegerField | default=42 | Altura en Us |
power_consumption | FloatField | default=0.0 | Consumo en kWh |
status | CharField(50) | choices, default=‘active’ | active, planned, deprecated, maintenance |
organization_id | FK → Organization | CASCADE | |
parent_rack_id | FK → self | SET_NULL, nullable | Rack padre (topología) |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now | |
deleted_at | DateTimeField | nullable, db_index | Soft delete |
is_template | BooleanField | default=False, db_index | Si es plantilla |
M2M: groups → RackGroup (through: racks_rack_groups, related_name: racks)
4.2.5 Device
Tabla: racks_device
Dispositivo dentro de un rack.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
rack_id | FK → Rack | CASCADE | Rack contenedor |
name | CharField(100) | NOT NULL | Nombre del dispositivo |
u_position | IntegerField | NOT NULL | Posición U inferior |
u_height | IntegerField | default=1 | Altura en Us |
notes | TextField | blank | Notas |
model_data | TextField | blank, default=’{}’ | JSON con metadatos (half_width, slot, stencil info) |
status | CharField(20) | choices, default=‘unknown’ | online, offline, warning, unknown |
last_status_check | DateTimeField | nullable | Último check |
management_config | TextField | blank, default=’{}’ | JSON con credenciales/IP (valores sensibles cifrados con Fernet) |
M2M: groups → RackGroup (through: racks_device_groups, related_name: devices)
4.2.6 ConfigBackup
Tabla: racks_configbackup
Backups de configuración de dispositivos de red.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
device_id | FK → Device | CASCADE | Dispositivo |
rack_id | FK → Rack | CASCADE | Rack |
config_text | TextField | NOT NULL | Running-config completo |
config_hash | CharField(64) | NOT NULL | SHA256 para detección de cambios |
backup_type | CharField(20) | choices, default=‘auto’ | auto, manual, pre_change |
created_at | DateTimeField | auto_now_add, db_index | |
created_by_id | FK → User | SET_NULL, nullable | |
vendor | CharField(50) | blank | Vendor (cisco_ios, etc.) |
device_version | CharField(100) | blank | Versión IOS, etc. |
is_active | BooleanField | default=True | |
notes | TextField | blank |
Indexes:
(device, -created_at)(rack, -created_at)
Meta: ordering = ['-created_at']
4.3 App blueprints
4.3.1 Blueprint
Tabla: blueprints_blueprint
Mapa de canvas infinito (plano o topología).
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
name | CharField(100) | NOT NULL | Nombre |
image_path | CharField(200) | blank | Ruta imagen de fondo |
scale | FloatField | default=1.0 | Escala del mapa |
bg_opacity | FloatField | default=1.0 | Opacidad del fondo |
show_bg | BooleanField | default=True | Mostrar fondo |
routing_mode | CharField(20) | default=‘manhattan’ | Modo de enrutamiento cables |
dark_mode | BooleanField | default=False | Modo oscuro |
cable_spread | IntegerField | default=12 | Separación de cables |
cable_curvature | FloatField | default=1.0 | Curvatura de cables |
cable_width | FloatField | default=2.0 | Grosor de cables |
organization_id | FK → Organization | CASCADE | |
created_at | DateTimeField | auto_now_add | |
deleted_at | DateTimeField | nullable, db_index | Soft delete |
4.3.2 BlueprintPlacement
Tabla: blueprints_blueprintplacement
Posición de un rack sobre un plano.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
blueprint_id | FK → Blueprint | CASCADE | Plano |
rack_id | FK → Rack | CASCADE | Rack posicionado |
pos_x | FloatField | default=0.0 | Posición X |
pos_y | FloatField | default=0.0 | Posición Y |
rotation | IntegerField | default=0 | Rotación en grados |
style_props | TextField | blank, default=’{}’ | JSON con propiedades visuales |
Constraints: unique_together = ('blueprint', 'rack')
4.3.3 MapAnnotation
Tabla: blueprints_mapannotation
Paredes, texto, zonas en el mapa.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
blueprint_id | FK → Blueprint | CASCADE | Plano |
type | CharField(50) | NOT NULL | wall, text, zone |
data | TextField | NOT NULL | JSON con coordenadas |
created_at | DateTimeField | auto_now_add |
4.3.4 AIPrompt
Tabla: blueprints_aiprompt
Prompts de AI Brain guardados (sistema Save As).
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
name | CharField(100) | NOT NULL | Nombre del prompt |
content | TextField | NOT NULL | Contenido del prompt |
is_default | BooleanField | default=False | Si es el prompt por defecto |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now |
Constraints: unique_together = [('organization', 'name')]
Meta: ordering = ['-updated_at']
4.4 App network
4.4.1 VendorProfile
Tabla: vendor_profiles (custom db_table)
Base de datos centralizada de vendors de red. Scope global (no per-organization).
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
| Identidad | |||
slug | SlugField(50) | UNIQUE | Clave única (cisco, juniper) |
display_name | CharField(100) | NOT NULL | Nombre visible |
aliases | ArrayField(CharField(100)) | default=list | Nombres alternativos para matching |
| SNMP | |||
snmp_communities | ArrayField(CharField(100)) | default=list | Communities vendor-specific |
sys_descr_patterns | ArrayField(CharField(500)) | default=list | Regex para detectar vendor en sysDescr |
sys_descr_model_regex | CharField(500) | blank | Regex para extraer modelo |
sys_descr_version_regex | CharField(500) | blank | Regex para extraer versión OS |
sys_object_id_prefix | CharField(255) | blank | Prefijo sysObjectID |
| MIB | |||
mib_modules | ArrayField(CharField(100)) | default=list | MIBs vendor-specific |
deep_discovery_oids | JSONField | default=dict | OIDs para deep discovery |
monitoring_oids | JSONField | default=dict | OIDs para monitoreo continuo |
fast_poll_oids | JSONField | default=dict | OIDs ligeros para fast polling (15s) |
| HTTP Fingerprinting | |||
http_server_patterns | ArrayField(CharField(500)) | default=list | Patrones HTTP Server header |
http_body_patterns | ArrayField(CharField(500)) | default=list | Patrones HTTP body |
http_product_lines | JSONField | default=dict | Mapa keyword → product line |
http_firmware_regex | CharField(500) | blank | Regex para firmware desde HTTP |
| SSH | |||
scrapli_platform | CharField(50) | default=‘generic’ | Identificador Scrapli |
ssh_show_version_cmd | CharField(200) | default=‘show version’ | |
ssh_show_interfaces_cmd | CharField(200) | default=‘show interfaces’ | |
ssh_show_inventory_cmd | CharField(200) | blank, default=‘show inventory’ | |
ssh_model_regex | CharField(500) | blank | Regex modelo desde show version |
ssh_version_regex | CharField(500) | blank | Regex versión desde show version |
| Device Type | |||
default_device_type | CharField(50) | choices, default=‘other’ | router, switch, firewall, load_balancer, wireless_controller, access_point, other |
device_type_keywords | JSONField | default=dict | Mapa tipo → keywords |
os_types | ArrayField(CharField(100)) | default=list | OS conocidos |
| Metadata | |||
priority | IntegerField | default=100 | Orden de detección (menor = primero) |
is_active | BooleanField | default=True | |
notes | TextField | blank | |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now |
Meta: ordering = ['priority', 'display_name']
4.4.2 CustomMib
Tabla: custom_mibs (custom db_table)
MIBs custom subidos por usuarios para enriquecer Deep Discovery.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
vendor_profile_id | FK → VendorProfile | CASCADE | Vendor asociado |
module_name | CharField(100) | NOT NULL | Nombre del módulo MIB |
original_filename | CharField(255) | NOT NULL | Nombre del archivo original |
source_content | TextField | blank | Fuente ASN.1 como backup |
file_size | IntegerField | default=0 | Tamaño en bytes |
compiled | BooleanField | default=False | Si está compilado |
compile_error | TextField | blank | Error de compilación |
extracted_oids | JSONField | default=list | OIDs extraídos |
uploaded_by_id | FK → User | SET_NULL, nullable | |
uploaded_at | DateTimeField | auto_now_add | |
organization_id | FK → Organization | CASCADE |
Constraints: unique_together = [['vendor_profile', 'module_name']]
Meta: ordering = ['-uploaded_at']
4.4.3 DeviceProfile
Tabla: device_profiles (custom db_table)
Perfil completo de dispositivo de red descubierto via Auto-Provision Tool.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
| Identificación | |||
organization_id | FK → Organization | CASCADE | |
ip_address | GenericIPAddressField | db_index | IP del dispositivo |
hostname | CharField(255) | blank | Hostname |
fqdn | CharField(500) | blank | FQDN |
| Sistema | |||
vendor | CharField(100) | blank, db_index | Fabricante |
model | CharField(200) | blank, db_index | Modelo |
serial_number | CharField(200) | blank | Número de serie |
mac_address | CharField(17) | blank | MAC principal |
| Software | |||
os_type | CharField(100) | blank | Tipo de OS |
os_version | CharField(200) | blank | Versión OS |
firmware_version | CharField(200) | blank | Versión firmware |
| Capacidades | |||
device_type | CharField(50) | choices, default=‘other’ | Tipo inferido |
capabilities | ArrayField(CharField(50)) | default=list | Capabilities |
| Protocolos | |||
supports_snmp | BooleanField | default=False | |
supports_ssh | BooleanField | default=False | |
supports_telnet | BooleanField | default=False | |
supports_netconf | BooleanField | default=False | |
supports_restconf | BooleanField | default=False | |
| SNMP | |||
snmp_version | CharField(10) | blank | v2c, v3 |
snmp_community | CharField(100) | blank, nullable | Community string |
snmp_v3_username | CharField(100) | blank | USM username |
snmp_v3_auth_protocol | CharField(20) | blank | MD5, SHA, SHA256, etc. |
snmp_v3_auth_key | CharField(255) | blank | Auth passphrase |
snmp_v3_priv_protocol | CharField(20) | blank | DES, AES128, etc. |
snmp_v3_priv_key | CharField(255) | blank | Privacy passphrase |
snmp_sys_descr | TextField | blank | sysDescr |
snmp_sys_object_id | CharField(255) | blank | sysObjectID |
snmp_uptime | BigIntegerField | nullable | sysUpTime en ticks |
interfaces | JSONField | default=dict | ifTable walk completo |
| Hardware | |||
cpu_count | IntegerField | nullable | |
memory_total_mb | BigIntegerField | nullable | |
chassis_type | CharField(200) | blank | |
| Deep Discovery | |||
deep_snmp_data | JSONField | default=dict | Datos MIB-enriched |
mib_modules_loaded | ArrayField(CharField(100)) | default=list | MIBs usados en discovery |
| Stencil Matching | |||
suggested_stencil_id | IntegerField | nullable | ID del stencil sugerido (sin FK constraint) |
stencil_confidence | IntegerField | default=0 | Confianza 0-100% |
stencil_match_reason | TextField | blank | Razón del match |
| Topología | |||
lldp_neighbors | JSONField | default=list | Vecinos LLDP |
cdp_neighbors | JSONField | default=list | Vecinos CDP |
| Discovery Metadata | |||
discovery_method | CharField(20) | choices, default=‘snmp’ | snmp, ssh, snmp+ssh, manual |
confidence_score | IntegerField | default=0, db_index | Score 0-100% |
discovered_at | DateTimeField | auto_now_add | |
last_updated | DateTimeField | auto_now | |
last_verified | DateTimeField | nullable | |
| Raw Data | |||
raw_snmp_data | JSONField | default=dict | Dump SNMP para debug |
raw_ssh_data | JSONField | default=dict | Output SSH para debug |
| Links | |||
linked_monitoring_target_id | FK → MonitoringTarget | SET_NULL, nullable | |
linked_device_id | FK → Device | SET_NULL, nullable |
M2M: groups → RackGroup (through auto, related_name: device_profiles)
Constraints: unique_together = [['organization', 'ip_address']]
Indexes:
(organization, ip_address)(vendor, model)(confidence_score)(discovered_at)
Meta: ordering = ['-confidence_score', '-discovered_at']
4.4.4 PortConnection
Tabla: port_connections (custom db_table)
Mapeo de puertos de dispositivo a DeviceProfiles o Devices (switch-to-switch, etc.). Exactamente UNO de profile o connected_device debe estar presente (XOR constraint).
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
device_id | FK → Device | CASCADE | Dispositivo fuente |
port_key | CharField(10) | NOT NULL | Clave del puerto |
profile_id | OneToOneField → DeviceProfile | CASCADE, nullable | Perfil conectado (XOR) |
connected_device_id | FK → Device | CASCADE, nullable | Dispositivo conectado (XOR) |
connected_port_key | CharField(10) | blank, default=” | Puerto destino |
cable_type | CharField(30) | blank, default=” | Tipo de cable |
cable_color | CharField(20) | blank, default=” | Color del cable |
cable_length | CharField(20) | blank, default=” | Longitud del cable |
notes | CharField(255) | blank | |
created_at | DateTimeField | auto_now_add |
Constraints:
unique_together = [('device', 'port_key')]CheckConstraint:(profile NOT NULL AND connected_device NULL) OR (profile NULL AND connected_device NOT NULL)— nombre:port_conn_one_targetUniqueConstraint(fields=['connected_device', 'connected_port_key'], condition=Q(connected_port_key__gt=''))— nombre:port_conn_unique_dest_port
Indexes: (organization)
4.5 App monitoring
4.5.1 MonitoringTarget
Tabla: monitoring_monitoringtarget
Dispositivo o IP a monitorear.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
name | CharField(100) | NOT NULL | Nombre |
ip_address | GenericIPAddressField | NOT NULL | IP |
device_id | FK → Device | SET_NULL, nullable | Dispositivo de rack |
ping_enabled | BooleanField | default=True | ICMP ping |
snmp_enabled | BooleanField | default=True | SNMP bandwidth |
http_enabled | BooleanField | default=True | HTTP health check |
interval_seconds | IntegerField | default=60 | Cadencia de polling por target (floors por protocolo en el Agent) |
timeout_ms | IntegerField | default=5000 | Timeout |
enabled | BooleanField | default=True | |
config | JSONField | default=dict | Config adicional (SNMP community, OIDs, etc.) |
last_status | CharField(20) | choices, default=‘unknown’ | unknown, up, down, degraded |
last_latency_ms | FloatField | nullable | |
last_packet_loss | FloatField | nullable | Pérdida de paquetes % |
last_check | DateTimeField | nullable | |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now | |
organization_id | FK → Organization | CASCADE |
Constraints: unique_together = ['organization', 'ip_address'] — el target es 1:1 por host y org. El campo scope fue eliminado (Ficha Central F4, migración monitoring/0025): la pertenencia a las páginas Wireless/UPS/Signage la decide DeviceProfile.assigned_page, no el target.
Meta: ordering = ['name'] · index (organization, enabled) (idx_target_org_enabled)
4.5.2 MetricSample
Tabla: monitoring_metricsample
Muestra individual de métrica (histórico). Nota: Candidato a eliminación futura — la mayoría de métricas se almacenan en VictoriaMetrics, pero este modelo aún tiene 38+ referencias activas.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
target_id | FK → MonitoringTarget | CASCADE | |
timestamp | DateTimeField | default=now, db_index | |
metric_type | CharField(20) | choices | latency, packet_loss, bandwidth_in, bandwidth_out, uptime, jitter |
value | FloatField | NOT NULL |
Indexes:
(target, metric_type, -timestamp)(timestamp)
Meta: ordering = ['-timestamp']
4.5.3 MonitoringAlert
Tabla: monitoring_monitoringalert
Configuración de alertas (per-device o global).
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
target_id | FK → MonitoringTarget | CASCADE, nullable | NULL para alertas globales |
organization_id | FK → Organization | CASCADE, nullable | |
is_global | BooleanField | default=False | Aplica a TODOS los targets |
name | CharField(100) | NOT NULL | |
condition_type | CharField(30) | choices | latency_above, latency_below, packet_loss_above, packet_loss_below, bandwidth_above, bandwidth_below, http_response_time_above, http_response_time_below, down_for |
threshold_value | FloatField | NOT NULL | Valor umbral |
threshold_duration_seconds | IntegerField | default=60 | Duración antes de trigger |
severity | CharField(10) | choices, default=‘red’ | green, blue, red |
enabled | BooleanField | default=True | |
last_triggered | DateTimeField | nullable | |
created_at | DateTimeField | auto_now_add |
Meta: ordering = ['name']
4.5.4 AlertEvent
Tabla: monitoring_alertevent
Historial de alertas disparadas.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
alert_id | FK → MonitoringAlert | CASCADE | |
target_id | FK → MonitoringTarget | SET_NULL, nullable | Target que disparó el evento |
triggered_at | DateTimeField | auto_now_add | |
resolved_at | DateTimeField | nullable | |
value_at_trigger | FloatField | NOT NULL | Valor cuando se disparó |
acknowledged | BooleanField | default=False | |
acknowledged_at | DateTimeField | nullable | |
acknowledged_by_id | FK → User | SET_NULL, nullable | |
notes | TextField | blank |
Meta: ordering = ['-triggered_at']
4.5.5 AggregatedMetric
Tabla: monitoring_aggregatedmetric
Métricas agregadas por hora para consultas históricas eficientes.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
target_id | FK → MonitoringTarget | CASCADE | |
hour | DateTimeField | db_index | Inicio del bucket horario |
metric_type | CharField(20) | choices | Tipo de métrica |
avg_value | FloatField | NOT NULL | Promedio |
min_value | FloatField | NOT NULL | Mínimo |
max_value | FloatField | NOT NULL | Máximo |
sample_count | IntegerField | NOT NULL | Número de muestras |
Constraints: unique_together = ['target', 'hour', 'metric_type']
Indexes: (target, metric_type, -hour)
Meta: ordering = ['-hour']
4.5.6 AIInsight
Tabla: monitoring_aiinsight
Diagnóstico AI generado por CreaRack Network Sentinel (CNS).
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
| Relaciones | |||
organization_id | FK → Organization | CASCADE | |
target_id | FK → MonitoringTarget | CASCADE | |
| Identificación | |||
incident_id | UUIDField | UNIQUE, auto uuid4 | ID de incidente |
case_number | PositiveIntegerField | nullable | Número de caso (auto-incrementado por org) |
| Diagnóstico | |||
summary | CharField(200) | NOT NULL | Resumen |
root_cause | TextField | NOT NULL | Causa raíz |
confidence_score | FloatField | NOT NULL | 0.0-1.0 |
osi_layer | IntegerField | nullable | Capa OSI 1-7 |
telemetry_insight | TextField | blank, default=” | |
| Recomendación | |||
action_label | CharField(200) | NOT NULL | Etiqueta de acción |
risk_level | CharField(10) | choices, default=‘LOW’ | LOW, MEDIUM, HIGH |
impact_description | TextField | blank, default=” | |
commands | JSONField | default=list | Comandos sugeridos |
rollback_commands | JSONField | default=list | Comandos de rollback |
| Estado | |||
status | CharField(20) | choices, default=‘pending’ | pending, executing, applied, acknowledged, expired, failed |
ai_provider | CharField(20) | NOT NULL | gemini, static |
| Contexto original | |||
raw_snmp_data | JSONField | nullable | |
raw_ssh_logs | TextField | nullable | |
anomaly_trigger | TextField | NOT NULL | |
| Ejecución | |||
applied_by_id | FK → User | SET_NULL, nullable | |
applied_at | DateTimeField | nullable | |
execution_output | TextField | nullable | |
rollback_executed | BooleanField | default=False | |
| Acknowledge | |||
acknowledged_by_id | FK → User | SET_NULL, nullable | |
acknowledged_at | DateTimeField | nullable | |
acknowledged_notes | TextField | blank, default=” | |
| Revisión | |||
is_revised | BooleanField | default=False | |
original_summary | CharField(200) | blank, default=” | |
original_root_cause | TextField | blank, default=” | |
revised_at | DateTimeField | nullable | |
revised_by_id | FK → User | SET_NULL, nullable | |
| ITSM | |||
sla_ack_deadline | DateTimeField | nullable | |
sla_resolve_deadline | DateTimeField | nullable | |
sla_ack_breached | BooleanField | default=False | |
sla_resolve_breached | BooleanField | default=False | |
resolved_at | DateTimeField | nullable | |
escalation_level | IntegerField | default=0 | |
escalated_at | DateTimeField | nullable | |
incident_group_id | FK → IncidentGroup | SET_NULL, nullable | |
known_issue_id | FK → KnownIssue | SET_NULL, nullable | |
suggested_runbook_id | FK → Runbook | SET_NULL, nullable | |
| Timestamps | |||
created_at | DateTimeField | auto_now_add | |
expires_at | DateTimeField | NOT NULL | Default: ahora + 4 horas |
Constraints:
UniqueConstraint(fields=['organization', 'case_number'], name='unique_case_number_per_org')
Indexes:
(organization, status)(target, -created_at)
Meta: ordering = ['-created_at']
4.5.7 AIInsightAuditLog
Tabla: monitoring_aiinsightauditlog
Log de auditoría para cada acción sobre un insight.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
insight_id | FK → AIInsight | CASCADE | |
action | CharField(30) | NOT NULL | created, applied, dry_run, acknowledged, rollback, expired |
user_id | FK → User | SET_NULL, nullable | |
details | JSONField | nullable | |
created_at | DateTimeField | auto_now_add |
Meta: ordering = ['-created_at']
4.5.8 InsightConversation
Tabla: monitoring_insightconversation
Conversación Q&A para un AI Insight (interacciones Explain).
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
insight_id | FK → AIInsight | CASCADE | |
question | TextField | NOT NULL | Pregunta del usuario |
answer | TextField | NOT NULL | Respuesta del AI |
ai_provider | CharField(20) | NOT NULL | |
asked_by_id | FK → User | SET_NULL, nullable | |
created_at | DateTimeField | auto_now_add |
Meta: ordering = ['created_at']
4.5.9 SLAPolicy
Tabla: monitoring_slapolicy
Políticas SLA configurables por nivel de riesgo.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
risk_level | CharField(10) | NOT NULL | HIGH, MEDIUM, LOW |
ack_minutes | IntegerField | default=30 | Minutos para acknowledgement |
resolve_minutes | IntegerField | default=240 | Minutos para resolución |
enabled | BooleanField | default=True |
Constraints: unique_together = ['organization', 'risk_level']
4.5.10 NotificationChannel
Tabla: monitoring_notificationchannel
Canal de notificación (webhook, email, Slack, Teams).
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
name | CharField(100) | NOT NULL | |
channel_type | CharField(20) | choices | webhook, email, slack, teams |
config | JSONField | default=dict | URL, headers, addresses, etc. |
risk_levels | JSONField | default=list | ["HIGH", "MEDIUM"] |
enabled | BooleanField | default=True | |
created_at | DateTimeField | auto_now_add |
4.5.11 NotificationLog
Tabla: monitoring_notificationlog
Log de notificaciones enviadas.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
channel_id | FK → NotificationChannel | CASCADE | |
insight_id | FK → AIInsight | CASCADE | |
event | CharField(30) | NOT NULL | created, sla_breached, escalated |
status | CharField(20) | NOT NULL | sent, failed, retrying |
response_code | IntegerField | nullable | |
error_message | TextField | blank, default=” | |
created_at | DateTimeField | auto_now_add |
Meta: ordering = ['-created_at']
4.5.12 EscalationPolicy
Tabla: monitoring_escalationpolicy
Política de escalamiento multi-nivel por nivel de riesgo.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
name | CharField(100) | NOT NULL | |
risk_level | CharField(10) | NOT NULL | |
enabled | BooleanField | default=True |
4.5.13 EscalationLevel
Tabla: monitoring_escalationlevel
Paso individual dentro de una política de escalamiento.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
policy_id | FK → EscalationPolicy | CASCADE | |
level | IntegerField | NOT NULL | 1, 2, 3… |
delay_minutes | IntegerField | NOT NULL | Minutos desde creación del insight |
notification_channel_id | FK → NotificationChannel | CASCADE |
Constraints: unique_together = ['policy', 'level']
Meta: ordering = ['level']
4.5.14 IncidentGroup
Tabla: monitoring_incidentgroup
Agrupa insights relacionados con la misma causa raíz.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
title | CharField(200) | NOT NULL | |
root_insight_id | FK → AIInsight | SET_NULL, nullable | Insight raíz |
status | CharField(20) | choices, default=‘open’ | open, resolved |
created_at | DateTimeField | auto_now_add | |
resolved_at | DateTimeField | nullable |
Meta: ordering = ['-created_at']
4.5.15 MaintenanceWindow
Tabla: monitoring_maintenancewindow
Ventana de mantenimiento para suprimir creación de insights.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
title | CharField(200) | NOT NULL | |
start_at | DateTimeField | NOT NULL | |
end_at | DateTimeField | NOT NULL | |
suppress_insights | BooleanField | default=True | |
suppress_notifications | BooleanField | default=True | |
created_by_id | FK → User | SET_NULL, nullable | |
notes | TextField | blank, default=” | |
created_at | DateTimeField | auto_now_add |
M2M: targets → MonitoringTarget (related_name: maintenance_windows) — vacío = todos los targets
Meta: ordering = ['-start_at']
4.5.16 KnownIssue
Tabla: monitoring_knownissue
Issue conocido con resolución documentada.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
title | CharField(200) | NOT NULL | |
description | TextField | NOT NULL | |
resolution | TextField | NOT NULL | |
match_keywords | JSONField | default=list | Keywords para auto-matching |
risk_level | CharField(10) | blank, default=” | |
occurrence_count | IntegerField | default=0 | |
last_seen | DateTimeField | nullable | |
created_by_id | FK → User | SET_NULL, nullable | |
created_at | DateTimeField | auto_now_add |
Meta: ordering = ['-occurrence_count']
4.5.17 InsightLink
Tabla: monitoring_insightlink
Link manual entre dos insights relacionados.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
source_id | FK → AIInsight | CASCADE | |
target_id | FK → AIInsight | CASCADE | |
link_type | CharField(10) | choices | parent, related |
created_by_id | FK → User | SET_NULL, nullable | |
created_at | DateTimeField | auto_now_add |
Constraints: unique_together = ['source', 'target']
4.5.18 Runbook
Tabla: monitoring_runbook
Librería de procedimientos de remediación.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
title | CharField(200) | NOT NULL | |
description | TextField | NOT NULL | |
steps | JSONField | default=list | [{"step": 1, "action": "...", "commands": [...]}] |
applicable_triggers | JSONField | default=list | Keywords de anomaly_trigger |
vendor | CharField(50) | blank, default=” | cisco_ios, juniper, '' = genérico |
created_by_id | FK → User | SET_NULL, nullable | |
usage_count | IntegerField | default=0 | |
created_at | DateTimeField | auto_now_add |
Meta: ordering = ['-usage_count']
4.5.19 RecurringPattern
Tabla: monitoring_recurringpattern
Patrón de incidentes recurrentes detectado para alertas proactivas.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
target_id | FK → MonitoringTarget | CASCADE | |
anomaly_trigger | CharField(200) | NOT NULL | |
pattern_type | CharField(30) | NOT NULL | daily, weekly, hourly |
description | CharField(200) | NOT NULL | |
occurrence_times | JSONField | default=list | |
confidence | FloatField | NOT NULL | 0.0-1.0 |
insight_count | IntegerField | NOT NULL | |
first_seen | DateTimeField | NOT NULL | |
last_seen | DateTimeField | NOT NULL | |
acknowledged | BooleanField | default=False | |
created_at | DateTimeField | auto_now_add |
Meta: ordering = ['-confidence']
4.6 App signage
4.6.1 SignageVendorAdapter
Tabla: signage_signagevendoradapter
Configuración de adaptador vendor-specific para comunicación con dispositivos de signage. Scope global (sin FK a Organization).
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
name | CharField(100) | UNIQUE | Nombre del adaptador |
slug | SlugField(50) | UNIQUE | |
vendor_profile_id | FK → VendorProfile | SET_NULL, nullable | |
adapter_type | CharField(20) | choices | json_rpc, rest_api, snmp_only |
api_url_template | CharField(500) | blank, default=” | Template URL con {ip} |
auth_type | CharField(20) | choices, default=‘basic’ | none, basic, bearer, api_key, oauth2, digest |
capabilities | JSONField | default=dict | {"push_content": true, ...} |
content_push_method | CharField(20) | choices, default=‘none’ | webdav, rest_upload, cloud_api, smil_http, none |
display_control_protocol | CharField(20) | choices, default=‘none’ | none, mdc, lg_protocol, pjlink, cip, sicp |
display_control_port | IntegerField | nullable | |
enabled | BooleanField | default=True |
Meta: ordering = ['name']
4.6.2 SignagePlayer
Tabla: signage_signageplayer
Reproductor/player de signage físico vinculado a network discovery.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
publish_token | UUIDField | UNIQUE, auto uuid4, no editable | Token para URL pública de contenido |
organization_id | FK → Organization | CASCADE | |
display_name | CharField(200) | NOT NULL | |
location | CharField(200) | blank, default=” | Ubicación física |
device_profile_id | FK → DeviceProfile | SET_NULL, nullable | |
monitoring_target_id | FK → MonitoringTarget | SET_NULL, nullable | |
adapter_id | FK → SignageVendorAdapter | SET_NULL, nullable | |
api_credentials | JSONField | default=dict | Cifrado via CredentialManager |
display_info | JSONField | default=dict | Resolución, modelo, firmware |
status | CharField(20) | choices, default=‘offline’ | online, offline, error, maintenance |
current_playlist_id | FK → Playlist | SET_NULL, nullable | |
current_schedule_id | FK → Schedule | SET_NULL, nullable | |
last_screenshot | CharField(500) | blank, default=” | |
last_heartbeat | DateTimeField | nullable | |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now |
M2M: groups → RackGroup (related_name: signage_players)
Constraints: unique_together = ['organization', 'device_profile']
Meta: ordering = ['display_name']
4.6.3 SignageOperation
Tabla: signage_signageoperation
Log de auditoría de operaciones de signage con rollback.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
player_id | FK → SignagePlayer | CASCADE | |
operation_type | CharField(20) | choices | publish, pause, resume, black, test, corporate, clear, restart |
previous_state | JSONField | default=dict | Estado antes de la operación |
new_state | JSONField | default=dict | Lo que se aplicó |
description | CharField(300) | blank, default=” | |
performed_by_id | FK → User | SET_NULL, nullable | |
created_at | DateTimeField | auto_now_add |
Meta: ordering = ['-created_at']
Auto-purge: Mantiene últimas 50 por organización.
4.6.4 MediaAsset
Tabla: signage_mediaasset
Contenido multimedia subido para playlists de digital signage.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
name | CharField(255) | NOT NULL | |
file_path | CharField(500) | blank, default=” | Ruta relativa bajo MEDIA_ROOT/signage/ |
thumbnail_path | CharField(500) | blank, default=” | |
asset_type | CharField(10) | choices, default=‘image’ | image, video, html5, url, stream |
status | CharField(20) | choices, default=‘uploading’ | uploading, processing, ready, error |
mime_type | CharField(100) | blank, default=” | |
file_size | BigIntegerField | default=0 | Bytes |
duration | FloatField | nullable | Segundos (video/stream) |
resolution | CharField(20) | blank, default=” | ej: 1920x1080 |
metadata | JSONField | default=dict | Codec, bitrate, etc. |
transcoded_variants | JSONField | default=dict | {"1080p": "path", ...} |
tags | ArrayField(CharField(50)) | default=list | Etiquetas |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now |
Meta: ordering = ['-created_at']
4.6.5 Playlist
Tabla: signage_playlist
Colección ordenada de media assets con timing y transiciones.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
name | CharField(200) | NOT NULL | |
description | TextField | blank, default=” | |
items | JSONField | default=list | [{asset_id, duration_seconds, transition, order}] |
total_duration | FloatField | default=0 | Segundos (auto-calculado) |
loop | BooleanField | default=True | |
is_auto | BooleanField | default=False | Auto-generada desde Content publish |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now |
Meta: ordering = ['-updated_at']
Auto-purge: Elimina auto-playlists >24h (mantiene 20 más recientes).
4.6.6 Schedule
Tabla: signage_schedule
Reglas basadas en tiempo para mapear playlists a franjas horarias.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
name | CharField(200) | NOT NULL | |
description | TextField | blank, default=” | |
rules | JSONField | default=list | [{type, start, end, days, playlist_id, priority}] |
default_playlist_id | FK → Playlist | SET_NULL, nullable | |
timezone | CharField(50) | default=‘UTC’ | |
valid_from | DateTimeField | nullable | |
valid_until | DateTimeField | nullable | |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now |
Meta: ordering = ['name']
4.6.7 ClientProject
Tabla: signage_clientproject
Proyecto de cliente que agrupa dispositivos, contenido, playlists y schedules.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
name | CharField(200) | NOT NULL | |
client_name | CharField(200) | blank, default=” | |
client_email | EmailField | blank, default=” | |
client_phone | CharField(50) | blank, default=” | |
description | TextField | blank, default=” | |
share_token | UUIDField | UNIQUE, auto uuid4, no editable | Token público para portal |
pin | CharField(20) | blank, default=” | PIN opcional |
permissions | JSONField | default=dict | {"upload": true, ...} |
is_active | BooleanField | default=True | |
expires_at | DateTimeField | nullable | |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now |
M2M:
devices→DeviceProfile(related_name:client_projects)playlists→Playlist(related_name:client_projects)
Meta: ordering = ['name']
4.6.8 PlaybackLog
Tabla: signage_playbacklog
Proof-of-play: registro de lo que se mostró, cuándo y por cuánto tiempo.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
player_id | FK → SignagePlayer | CASCADE | |
asset_id | FK → MediaAsset | SET_NULL, nullable | |
playlist_id | FK → Playlist | SET_NULL, nullable | |
started_at | DateTimeField | NOT NULL | |
ended_at | DateTimeField | nullable | |
duration | FloatField | nullable | Duración real en segundos |
verification_method | CharField(20) | choices, default=‘api’ | api, screenshot, heartbeat, manual |
screenshot_path | CharField(500) | blank, default=” |
Indexes:
(organization, -started_at)(player, -started_at)
Meta: ordering = ['-started_at']
4.6.9 ContentDeployment
Tabla: signage_contentdeployment
Tracking de despliegue de playlists/schedules a players.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
playlist_id | FK → Playlist | SET_NULL, nullable | |
schedule_id | FK → Schedule | SET_NULL, nullable | |
status | CharField(20) | choices, default=‘pending’ | pending, deploying, deployed, failed, partial |
deployment_results | JSONField | default=dict | Resultados por player |
error_message | TextField | blank, default=” | |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now |
M2M: players → SignagePlayer (related_name: deployments)
Meta: ordering = ['-created_at']
4.6.10 ClientShareLink
Tabla: signage_clientsharelink
Link de acceso basado en token para clientes externos del portal de signage.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
organization_id | FK → Organization | CASCADE | |
token | UUIDField | UNIQUE, auto uuid4, no editable, db_index | |
name | CharField(200) | NOT NULL | |
client_name | CharField(200) | blank, default=” | |
client_email | EmailField | blank, default=” | |
pin | CharField(10) | blank, default=” | PIN opcional (4-6 dígitos) |
permission_level | CharField(20) | choices, default=‘playlist’ | content_only, playlist, schedule, view_only |
require_approval | BooleanField | default=False | Cambios van a cola de aprobación |
is_active | BooleanField | default=True | |
expires_at | DateTimeField | nullable | NULL = no expira |
last_accessed | DateTimeField | nullable | |
access_count | IntegerField | default=0 | |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now | |
created_by_id | FK → User | SET_NULL, nullable |
M2M:
players→SignagePlayer(related_name:share_links)playlists→Playlist(related_name:share_links)schedules→Schedule(related_name:share_links)
Meta: ordering = ['-created_at']
4.6.11 PortalChangeLog
Tabla: signage_portalchangelog
Audit trail y cola de cambios pendientes del portal de clientes.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
share_link_id | FK → ClientShareLink | CASCADE | |
action | CharField(20) | choices | upload, replace, reorder, duration, schedule, delete |
status | CharField(20) | choices, default=‘applied’ | applied, pending, approved, rejected |
details | JSONField | default=dict | Qué cambió |
pending_payload | JSONField | default=dict | Payload para aplicar al aprobar |
reviewed_by_id | FK → User | SET_NULL, nullable | |
reviewed_at | DateTimeField | nullable | |
review_note | TextField | blank, default=” | |
ip_address | GenericIPAddressField | nullable | |
created_at | DateTimeField | auto_now_add |
Indexes: (status, -created_at)
Meta: ordering = ['-created_at']
4.7 App terminal
4.7.1 Script
Tabla: terminal_script
Scripts de comandos de red.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
name | CharField(100) | NOT NULL | |
vendor | CharField(50) | blank, default=‘Generic’ | |
category | CharField(50) | default=‘General’ | |
description | TextField | blank | |
is_safe | BooleanField | default=False | Si es seguro ejecutar |
commands | JSONField | default=list | ["cmd1", "cmd2"] |
created_by_id | FK → User | SET_NULL, nullable | |
organization_id | FK → Organization | CASCADE | |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now |
4.7.2 AgentInstance
Tabla: terminal_agentinstance
Representa una instalación del Local Agent conectada al SaaS. Soporta roles Primary/Secondary para failover de Sentinel Mode.
| Campo | Tipo | Constraints | Descripción |
|---|---|---|---|
id | AutoField | PK | |
agent_id | CharField(64) | UNIQUE, db_index | ID único del agente |
organization_id | FK → Organization | CASCADE | |
role | CharField(16) | choices, default=‘secondary’ | primary, secondary |
status | CharField(16) | choices, default=‘offline’ | online, offline |
hostname | CharField(255) | blank, default=” | |
ip_address | GenericIPAddressField | nullable | |
agent_version | CharField(32) | blank, default=” | |
sentinel_active | BooleanField | default=False | |
last_seen | DateTimeField | nullable | |
connected_at | DateTimeField | nullable | |
disconnected_at | DateTimeField | nullable | |
created_at | DateTimeField | auto_now_add | |
updated_at | DateTimeField | auto_now |
Indexes:
(organization, role)(organization, status)
Meta: ordering = ['-connected_at']
5. Diagrama de Relaciones entre Entidades
┌─────────────────────────────────────────────────────────────────────┐
│ CORE (Tenant Root) │
│ │
│ ┌──────────┐ ┌──────────────┐ ┌──────┐ │
│ │ SaaSModule│◄──M2M──┤ Plan │ │ │ │
│ └──────────┘ └──────┬───────┘ │ │ │
│ ▲ │FK │ │ │
│ │M2M ┌────▼────────┐ │ │ │
│ └─────────────┤Organization │◄─┘ │ │
│ └──┬──┬──┬────┘ │ │
│ │ │ │ │ │
│ ┌──────────────────┘ │ └──────────┐ │ │
│ │FK │FK │FK │ │
│ ┌──▼──┐ ┌──────────┐ │ ┌───────────▼─┐ │ ┌──────────────┐ │
│ │User │ │SystemLog │ │ │StoredCred │ │ │ LoginLog │ │
│ └──┬───┘ └──────────┘ │ └─────────────┘ │ └──────────────┘ │
│ │ │ │ │
│ │1:1 │ │ │
│ ┌──▼──────────────┐ │ │ │
│ │ModulePermission │ │ │ │
│ └─────────────────┘ │ │ │
└───────────────────────────┼───────────────────┼─────────────────────┘
│ │
┌───────────────────────────┼───────────────────┼─────────────────────┐
│ RACKS │ │ │
│ │ │ │
│ ┌──────────┐ ┌────────▼──┐ ┌──────────▼──┐ │
│ │RackGroup │◄──M2M──┤ Rack │ │ Stencil │ │
│ └────┬─────┘ └──┬────────┘ └─────────────┘ │
│ │ │FK │
│ │M2M ┌─────▼────┐ ┌──────────────┐ │
│ └────────┤ Device ├───►ConfigBackup │ │
│ └─────┬─────┘ └──────────────┘ │
│ │FK │
└──────────────────────┼──────────────────────────────────────────────┘
│
┌──────────────────────┼──────────────────────────────────────────────┐
│ BLUEPRINTS │
│ │ │
│ ┌──────────┐ ┌────▼────────────────┐ ┌──────────────┐ │
│ │AIPrompt │ │BlueprintPlacement │ │MapAnnotation │ │
│ └──────────┘ └────┬────────────────┘ └──────┬───────┘ │
│ │FK │FK │
│ ┌────▼───────┐ │ │
│ │ Blueprint │◄───────────────────┘ │
│ └────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ NETWORK │
│ │
│ ┌──────────────┐ ┌───────────────┐ ┌──────────────┐ │
│ │VendorProfile │◄──FK──┤ CustomMib │ │PortConnection │ │
│ └──────────────┘ └───────────────┘ └──────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ DeviceProfile │ │
│ │ FK → Organization │ │
│ │ FK → MonitoringTarget (opt) │ │
│ │ FK → Device (opt) │ │
│ │ M2M → RackGroup │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ MONITORING │
│ │
│ ┌────────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │MonitoringTarget │◄──FK──┤MetricSample │ │AggregatedMetric│ │
│ └───────┬────────┘ └──────────────┘ └──────────────┘ │
│ │FK │
│ │ ┌──────────────────┐ │
│ ├────────►│ AIInsight │◄──FK── IncidentGroup │
│ │ │ FK → KnownIssue │◄──FK── Runbook │
│ │ └────────┬─────────┘ │
│ │ │FK │
│ │ ┌─────────────┼──────────────┐ │
│ │ │ │ │ │
│ │ AuditLog Conversation InsightLink │
│ │ │
│ │ ┌───────────────┐ ┌──────────────────┐ │
│ ├───►│MonitoringAlert │───►│ AlertEvent │ │
│ │ └───────────────┘ └──────────────────┘ │
│ │ │
│ ┌───────┴───────────────────────────────────────────┐ │
│ │ ITSM: SLAPolicy, NotificationChannel, │ │
│ │ EscalationPolicy → EscalationLevel, │ │
│ │ MaintenanceWindow (M2M targets), │ │
│ │ KnownIssue, Runbook, RecurringPattern │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ SIGNAGE │
│ │
│ ┌────────────────────┐ ┌────────────────┐ │
│ │SignageVendorAdapter │ │ MediaAsset │ │
│ │ (global) │ └───────┬────────┘ │
│ └────────┬───────────┘ │FK │
│ │FK │ │
│ ┌────────▼───────────┐ ┌──────▼─────────┐ ┌──────────┐ │
│ │ SignagePlayer │◄──M2M──┤ContentDeployment│ │ Schedule │ │
│ │ FK → Playlist │ └────────────────┘ └──────────┘ │
│ │ FK → Schedule │ │
│ └────────┬───────────┘ ┌──────────┐ │
│ │FK │ Playlist │ │
│ ┌────────▼────────┐ └──────────┘ │
│ │SignageOperation │ │
│ │PlaybackLog │ ┌──────────────────┐ │
│ └─────────────────┘ │ClientShareLink │ │
│ │ M2M → Players │ │
│ ┌──────────────┐ │ M2M → Playlists │ │
│ │ClientProject │ │ M2M → Schedules │ │
│ │ M2M → Devices │ └────────┬─────────┘ │
│ │ M2M → Playlists│ │FK │
│ └──────────────┘ ┌────────▼─────────┐ │
│ │PortalChangeLog │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ TERMINAL │
│ │
│ ┌──────────┐ ┌───────────────┐ │
│ │ Script │ │AgentInstance │ │
│ │ FK→Org │ │ FK→Org │ │
│ │ FK→User │ │ unique agent_id│ │
│ └──────────┘ └───────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
6. Patrones de Diseño Clave
6.1 Soft Delete (deleted_at)
Tres modelos implementan borrado lógico: Organization, Rack y Blueprint. Cada uno tiene:
- Campo
deleted_at(DateTimeField, nullable) - Métodos
soft_delete()yrestore() - Propiedad
is_deleted - Los queries regulares no filtran automáticamente por
deleted_at— el filtrado se hace en las vistas/API
6.2 Campos JSON
El uso de JSONField es extensivo para datos semi-estructurados:
| Modelo | Campo | Uso |
|---|---|---|
Device.model_data | TextField (JSON manual) | Metadatos del dispositivo (half_width, slot, stencil) |
Device.management_config | TextField (JSON manual) | IP, credenciales cifradas, vendor |
Organization.sentinel_config | JSONField | Configuración de Sentinel Mode |
DeviceProfile.interfaces | JSONField | ifTable SNMP walk completo |
DeviceProfile.deep_snmp_data | JSONField | Datos de deep discovery MIB-enriched |
DeviceProfile.lldp_neighbors | JSONField | Vecinos LLDP (lista) |
MonitoringTarget.config | JSONField | Config adicional SNMP |
AIInsight.commands | JSONField | Comandos sugeridos (lista) |
Playlist.items | JSONField | Items de playlist (lista de objetos) |
Schedule.rules | JSONField | Reglas de scheduling (lista) |
StoredCredential.encrypted_data | JSONField | Datos cifrados Fernet |
Nota: Device.model_data y Device.management_config usan TextField con JSON manual (legacy) en lugar de JSONField nativo de PostgreSQL.
6.3 Cifrado Fernet (AES-128-CBC)
El modelo StoredCredential almacena credenciales sensibles cifradas con Fernet (basado en AES-128-CBC + HMAC-SHA256). Implementado via core.security.credential_manager.CredentialManager.
Campos cifrados:
community(SNMP v2c)auth_key,priv_key(SNMPv3)ssh_password,enable_password(SSH)http_password(HTTP)
Adicionalmente, Device.management_config puede contener valores cifrados con Fernet (detectados por el prefijo gAAAA).
6.4 Audit Logging
El sistema tiene múltiples capas de auditoría:
| Nivel | Modelo | Propósito |
|---|---|---|
| Sistema | SystemLog | Acciones generales (AUTH, RACK, etc.) |
| Login | LoginLog | Cada intento de login (v1.0.48) |
| Impersonación | ImpersonationLog | Sesiones de superusuario |
| CNS | AIInsightAuditLog | Acciones sobre insights |
| Signage | SignageOperation | Operaciones de publish/clear (auto-purge 50) |
| Portal | PortalChangeLog | Cambios del cliente externo |
| Notificaciones | NotificationLog | Envíos de notificaciones ITSM |
6.5 ArrayField (PostgreSQL-specific)
Los siguientes campos usan ArrayField de django.contrib.postgres:
| Modelo | Campo | Tipo Base |
|---|---|---|
VendorProfile.aliases | CharField(100) | |
VendorProfile.snmp_communities | CharField(100) | |
VendorProfile.sys_descr_patterns | CharField(500) | |
VendorProfile.mib_modules | CharField(100) | |
VendorProfile.http_server_patterns | CharField(500) | |
VendorProfile.http_body_patterns | CharField(500) | |
VendorProfile.os_types | CharField(100) | |
DeviceProfile.capabilities | CharField(50) | |
DeviceProfile.mib_modules_loaded | CharField(100) | |
MediaAsset.tags | CharField(50) |
6.6 Tablas con db_table Custom
| Modelo | db_table | Razón |
|---|---|---|
VendorProfile | vendor_profiles | Nomenclatura semántica |
CustomMib | custom_mibs | Nomenclatura semántica |
DeviceProfile | device_profiles | Nomenclatura semántica |
PortConnection | port_connections | Nomenclatura semántica |
El resto de modelos usan el naming convention de Django: {app_label}_{model_name_lower}.
7. Resumen de Migraciones
| App | Total Archivos | Migraciones Notables |
|---|---|---|
core | 24 | 0010_storedcredential (Fernet encryption), 0011_loginlog |
racks | 12 | 0001_initial (Organization, User, Rack, Device) |
blueprints | 9 | 0006_aiprompt (Brain Editor Save As) |
network | 56 | 0037_deviceprofile_snmpv3 (5 campos v3), 0038_deviceprofile_groups_m2m, 0039_portconnection (XOR constraint), 0040_ups_vendor_profiles (RunPython seed), 0055_deviceprofile_assigned_page (Ficha Central F4) |
monitoring | 26 | 0016_add_scope_to_monitoring_target (histórica — el scope se eliminó en 0025_ficha_central_remove_scope, Ficha Central F4), múltiples RunPython para ITSM models |
signage | 14 | 0006_signageoperation, 0007_playlist_is_auto |
terminal | 3 | 0001_initial, 0002_agentinstance |
| Total | 144 | (excluyendo __init__.py en cada carpeta = ~151 archivos) |
RunPython notables (data migrations):
- Seed de VendorProfiles para ~40 vendors de red (network)
- Seed de 9 UPS vendor profiles (network 0040)
- Seed de 12 SignageVendorAdapters (signage)
- Seed de SaaSModules y Plans (core)
8. Backup y Restore
Documentación detallada:
Documentation/admin/BACKUP_RESTORE.md
Backup PostgreSQL
# Desarrollo
docker compose exec db pg_dump -U crearack_user crearack_pro > backup.sql
# Producción
ssh root@crearack.com "docker exec crearack-pro-zcmvsl-db-1 \
pg_dump -U crearack_user crearack_pro" > backup_prod.sql
Restore
# Desarrollo
docker compose exec -T db psql -U crearack_user crearack_pro < backup.sql
# Producción (con precaución)
cat backup.sql | ssh root@crearack.com "docker exec -i crearack-pro-zcmvsl-db-1 \
psql -U crearack_user crearack_pro"
Backup via Django (con remapeo de IDs)
La app incluye endpoints de backup/restore nativos que manejan:
- Exportación de modelos seleccionados a JSON
- Remapeo de IDs al importar (evita colisiones)
- Preservación de relaciones (FKs, M2M, cables)
9. Notas de Rendimiento
Conexiones
| Parámetro | Desarrollo | Producción |
|---|---|---|
max_connections | 100 (default PG) | 500 (customizado) |
CONN_MAX_AGE | 0 (nueva por request) | 600s (reutilizar 10 min) |
CONN_HEALTH_CHECKS | No | Sí (Django 6) |
shared_buffers | 128MB (default) | 256MB |
Incidente de Saturación (01-04-2026)
El modo Sentinel con 268 targets saturó max_connections=100 tras reconexión post-downtime de 5 días. Solución temporal: subir a 500. Solución definitiva pendiente: pgbouncer como connection pooler en modo transaction.
Cuellos de Botella Conocidos
- MetricSample: Tabla de alto volumen (38+ referencias activas). Candidata a eliminación cuando se migre completamente a VictoriaMetrics.
- DeviceProfile: Modelo muy ancho (~50 campos) con múltiples JSONFields grandes (
interfaces,deep_snmp_data,raw_snmp_data). Considerar partición o archivado de raw data. - PlaybackLog / SignageOperation: Tablas de alto volumen con auto-purge (50 ops, 24h playlists).
- Sin pgbouncer: Cada worker de Daphne/Huey abre conexiones independientes a PostgreSQL.
Mejoras Pendientes
- pgbouncer: Connection pooler en modo
transactionentre Django y PostgreSQL - Sentinel batch status: Agrupar device status updates en bulk POST
- Migration squash: 44 migraciones en
network/— diferido a v2.0.0 por riesgo - MetricSample cleanup: Eliminar cuando VictoriaMetrics cubra todas las referencias
Mantenido por: Claude (Anthropic) + Equipo CreaRack
Véase también
- [[crearack-tech—architecture—pg18-migration-plan]]
- [[crearack-tech—admin—cache-and-database]]
- [[crearack-tech—guides—database-admin-guide]]
- [[decision—20260315—postgres-18-pgbouncer]]
- [[decision—20260201—conn-max-age-daphne]]
- [[concept—saas—multi-tenancy]]
- [[decision—20260101—django-ninja-vs-drf]]
Referenciado desde
- Backup & Restore - CreaRack Pro
- Cache y Base de Datos — Valkey + PostgreSQL
- Contexto Técnico del Proyecto - CreaRack Pro
- Estado de Tareas - CreaRack Pro v1.0.57
- Guía de Administración de Base de Datos — CreaRack Pro
- Guía de Optimización de Performance - CreaRack Pro
- Plan de Migración PostgreSQL 16 → 18
- Valkey Persistence Patterns - CreaRack Pro