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.jsonybackup_data.jsonpresentes.- Checksum SHA-256 de
backup_data.jsoncontra 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 conbuild_manifest()y lo escribe comomanifest.jsondentro del ZIP. Ver [[entity—racks—service—run-full-backup]].racks/tasks.py::run_full_backup()— llamaverify_backup_zip()tras generar el ZIP async; si falla, borra el ZIP y marca elAsyncJoben error.core/services/backup_service.py::generate_backup()— llamaverify_backup_zip()tras generar el backup automático nocturno; si falla, borra el ZIP y devuelveNone. 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]]