CreaRack-SL

DeviceTrashEntry · Modelo network

Propósito

Un “dispositivo” en CreaRack Pro no es una fila: son hasta tres modelos enlazados por la IP de gestión (DeviceProfile la ficha descubierta, MonitoringTarget el objetivo de sondeo, racks.Device la colocación en rack). Añadir soft-delete (deleted_at) a los tres obligaría a filtrarlo en cada consulta de esos modelos, incluidas las que usa el Local Agent. DeviceTrashEntry evita eso: al borrar, guarda una instantánea JSON con todo lo necesario para recrear las tres caras, y borra de verdad los originales. Restaurar = recrear desde la instantánea.

Límite honesto (documentado en el propio docstring del modelo): el histórico de métricas del MonitoringTarget no vuelve — se pierde en cascada al borrar, igual que ya pasaba con el borrado de Devices desde la v1.35.0.

Contrato

network/models/trash.py:

CampoTipoNotas
organizationFK → core.Organizationon_delete=CASCADE, related_name="device_trash_entries"
nameCharField(200)nombre del dispositivo en el momento del borrado
ip_addressCharField(45)IP de gestión; vacía si la entrada es solo una colocación en rack sin IP
kindCharField(20)assigned_page del perfil (wireless/ups/signage) o "rack"
snapshotJSONField{"profile": {...}|null, "targets": [...], "rack_devices": [...]}
deleted_atDateTimeField(auto_now_add=True)ordena la papelera (-deleted_at)
deleted_byFK → core.User, SET_NULLquién borró
deleted_fromCharField(40)superficie de origen (Observatory, Wireless, Rack Editor…)

Índice compuesto (organization, deleted_at). Propiedad summary arma un texto corto (“profile · monitoring · rack X”) a partir de lo que contenga la instantánea.

Dependencias

Qué usa — network/services/device_trash.py:

  • trash_device(org, ip=..., rack_device=..., user=..., source=...): localiza DeviceProfile + MonitoringTarget(s) + racks.Device(s) que comparten la IP, los serializa (_snapshot, que copia campos concretos y convierte FKs a <campo>_id) y los borra de verdad en orden (targets → colocaciones → ficha).
  • restore_entry(entry): recrea desde la instantánea lo que no exista ya; filtra la instantánea a los campos que el modelo tiene HOY (_restore_kwargs, tolera columnas retiradas desde el borrado). Cada pieza puede saltarse y quedar en result["skipped"] por tres motivos: la IP ya está ocupada por una ficha/target re-descubierto, la posición U del rack ya no está libre (_placement_conflict, valida con el mismo validador canónico que usa el Rack Editor, racks.utils.placement.validate_device_placements — cubre solape y que el rack no haya encogido), o el rack destino está en la papelera de armarios. Fix v1.125.0 (Ronda 30-08 Tanda 4, #281 pt.6): la búsqueda del rack destino (Rack.objects.filter(organization=org, id=d.get("rack_id"))) no comprobaba deleted_at__isnull=True, así que un equipo podía “renacer” dentro de un armario invisible (borrado pero no purgado). Ahora ese caso se salta con motivo ("no longer exists") igual que un rack purgado de verdad, y la entrada de papelera se conserva. Fix v1.96.0 (Tanda 7, #482): la entrada de papelera solo se borra si se recreó algo de verdad (result["restored"]); antes se borraba siempre al terminar, así que un restore que se saltaba todo (IP ocupada, rack desaparecido) destruía la única copia del equipo sin recrear nada — y POST /api/trash/devices/{id}/restore respondía igualmente "Device restored" con success: true. Ahora, si restored es false, la entrada sigue en la papelera y el endpoint responde success: false con el detalle de qué se saltó.

Quién la usa:

  • network/api/device_trash.py → POST /api/network/device-trash (trash_device_everywhere): entrada desde Observatory/Wireless/UPS/Signage y desde racks/services/racks.py al guardar un rack con dispositivos retirados.
  • racks/api/trash.py → GET /api/trash/devices, POST /api/trash/devices/{id}/restore, DELETE /api/trash/devices/{id}, DELETE /api/trash/devices (listar, restaurar, borrar una entrada, vaciar). Estos endpoints conviven con los ya existentes de racks y blueprints bajo el mismo router de papelera (GET /api/trash/count ahora suma también devices).

Ejemplo de uso

from network.services.device_trash import trash_device, restore_entry

entry = trash_device(org, ip="10.0.0.5", user=request.user, source="observatory")
# ...
result = restore_entry(entry)  # recrea lo que no exista ya; result["restored"] indica si de verdad recreó algo

Véase también

  • [[entity—network—model—deviceprofile]]
  • [[entity—monitoring—model—monitoringtarget]]
  • [[entity—racks—model—device]]
  • [[crearack—network—device-page]]
  • [[crearack—racks—trash-and-restore]]
  • [[concept—saas—multi-tenancy]]
  • [[decision—20260403—multi-tenancy-rls]]
  • [[feature—network—papelera-dispositivos]]