CreaRack-SL

Manifest y verificación de integridad de backups (core/services/backup_verify.py)

Propósito

Construye y verifica el manifiesto de integridad de los ZIP de backup (task #236, PR-1, v1.69.0). Todo ZIP de backup —manual (vía write_full_backup_zip) o automático nocturno (vía generate_backup)— incluye ahora un manifest.json con conteos por entidad, la lista de uploads empaquetados, y el SHA-256 de backup_data.json. Tras escribir el ZIP, verify_backup_zip() lo relee de disco y confirma que lo declarado coincide con lo que hay.

La comparación se hace contra el propio ZIP, NO contra la base de datos viva: en la creación sería redundante (misma fuente un instante antes) y una edición concurrente daría falsos rojos. El manifest es el “esperado” que consumirá la verificación del restore (PR-2 de la task #236, aún pendiente).

Funciones públicas

counts_from_backup_data(data: dict) → dict

Conteos por entidad de un backup_data ya parseado (12 colecciones top-level + devices, placements, annotations anidados bajo racks/blueprints).

build_manifest(backup_data: dict, json_bytes: bytes, upload_arcnames: list) → dict

Construye el manifest a partir de los MISMOS bytes que se escriben al ZIP (evita divergencia entre lo declarado y lo real por doble serialización). Campos: manifest_version, created_at, app_version, organization, counts, uploads (ordenados), backup_data_sha256.

verify_backup_zip(zip_path: str) → dict

Relee un ZIP de backup de disco y verifica su integridad. Devuelve {"ok": bool, "errors": [...], "counts": {...}} — nunca lanza por contenido inválido (todo error de contenido acaba en errors); sí propaga OSError si el fichero no se puede abrir.

Comprueba:

  • manifest.json y backup_data.json presentes.
  • Checksum SHA-256 de backup_data.json contra el declarado en el manifest.
  • Conteos por entidad declarados vs. reales.
  • Uploads declarados en el manifest presentes en el ZIP.
  • CRC de todos los miembros (zipf.testzip()) — caza ZIP truncado/corrupto en disco.

Consumidores

  • racks/api/export/backup.py::write_full_backup_zip() — construye el manifest con build_manifest() y lo escribe como manifest.json dentro del ZIP. Ver [[entity—racks—service—run-full-backup]].
  • racks/tasks.py::run_full_backup() — llama verify_backup_zip() tras generar el ZIP async; si falla, borra el ZIP y marca el AsyncJob en error.
  • core/services/backup_service.py::generate_backup() — llama verify_backup_zip() tras generar el backup automático nocturno; si falla, borra el ZIP y devuelve None. Ver [[entity—core—service—backup-service]].

Retrocompatibilidad

Los ZIP antiguos sin manifest.json no se ven afectados por este cambio: el restore actual ignora el fichero extra. verify_backup_zip() solo se invoca en la CREACIÓN de nuevos backups, no al restaurar backups existentes.

Tests

Archivo: tests/api/test_backup_verify.py (8 tests) — manifest consistente, manipulación del JSON detectada (checksum + conteos), upload declarado ausente, no-ZIP, ZIP legacy sin manifest, verificación en el result del job, job en error si la verificación falla, y paridad de formato automático=manual.

Véase también

  • [[entity—core—service—backup-service]]
  • [[entity—racks—service—run-full-backup]]
  • [[entity—racks—endpoint—backup-full-start]]
  • [[entity—racks—endpoint—backup-full-download]]
  • [[feature—racks—full-backup-tenant-scoping]]