Volver a la wiki

Servicio de Backup (core/services/backup_service.py)

Propósito

Gestión centralizada de backups por organización:

Funciones públicas

generate_backup(org: Organization) → str | None

Firma: generate_backup(org) -> str | None

Propósito: Genera un ZIP completo y VERIFICADO de una organización (datos + uploads). Devuelve None si la verificación de integridad falla — un backup que no se puede confiar es peor que uno visiblemente ausente.

Cambio v1.69.0 (task #236, PR-1): ya NO serializa los modelos inline. Delega en racks.api.export.backup.write_full_backup_zip() — el mismo punto único de generación que usa el backup manual — y verifica el resultado con core.services.backup_verify.verify_backup_zip().

Antes de v1.69.0: este módulo mantenía una copia propia del serializador (~230 líneas), que había derivado en silencio respecto al formato manual: faltaba script_templates, device_profiles recortados a 10 campos (de ~40), blueprints sin mode/sort_order, y credenciales SNMP en forma cifrada no restaurable. Nadie lo sabía porque nada lo comprobaba — el backup automático nocturno llevaba tiempo generando maletas incompletas.

Flujo actual:

  1. write_full_backup_zip(org, zip_path) — escribe datos + uploads + manifest.json (import local: evita ciclo con racks.api.export.backup, que a su vez importa helpers de este módulo).
  2. verify_backup_zip(zip_path) — relee el ZIP de disco.
  3. Si verification["ok"] es False: loguea los errores, borra el ZIP si existe, devuelve None.
  4. Si es True: loguea éxito, devuelve la ruta del ZIP.

Locación: core/services/backup_service.py línea ~92

Seguridad: Scoped por org (via write_full_backup_zip); solo empaqueta uploads referenciados (evita path escape)

get_backup_dir(org: Organization) → str

Propósito: Retorna (creando si falta) el directorio de backups de una org

Locación: core/services/backup_service.py línea ~80

cleanup_old_backups(org: Organization) → None

Propósito: Limpia backups más antiguos que N días (configurable)

Locación: core/services/backup_service.py (sin cambios en v1.69.0)

get_latest_backup(org: Organization) → str | None

Propósito: Obtiene la ruta del backup más reciente de una org

Locación: core/services/backup_service.py (sin cambios en v1.69.0)

Helpers de Scoping de Uploads (s107 · fix)

(sin cambios de comportamiento en v1.69.0 — viven en racks/api/export/backup.py, reusados por write_full_backup_zip(); ver [[entity—racks—service—run-full-backup]])

_media_relpath(image_path: str) → str | None

Normaliza variantes de rutas almacenadas a forma canónica uploads/.... Rechaza strings vacíos, paths fuera de uploads/ y paths no normalizables.

collect_org_upload_files(image_paths: list[str]) → list[tuple[str, str]]

Mapea paths almacenados → ficheros existentes en disco, scoped a los referenciados. Solo ficheros existentes, dedup, anti-escape (../../../etc/passwd bloqueado).

org_upload_image_paths(backup_data: dict) → list[str]

Extrae todos los image_path referenciados por una org a partir de su backup_data (stencils + blueprints).

Integración

Usado por:

Dependencias (desde v1.69.0):

Cambio principal (s107)

Antes: os.walk(MEDIA_ROOT/uploads) → ZIP con TODOS los uploads (todos los tenants) Después: collect_org_upload_files() → ZIP con uploads solo de la org

Impacto:

Tests

Archivo: tests/api/test_backup.py (scoping de uploads) + tests/api/test_backup_verify.py (8 tests, integridad y paridad automático=manual, task #236)

Véase también

Subir