feat: record backup snapshots during run completion

Upsert SnapshotRecord rows directly from run_pobsync_backup results so
new successful and failed backup runs are reflected in the database
without requiring a separate discovery pass. Keep discovery for existing
snapshots and repair workflows, and cover success, failure, and dry-run
behavior with tests.
This commit is contained in:
2026-05-19 11:09:20 +02:00
parent 336fb1a5be
commit 0a49c5719c
3 changed files with 140 additions and 11 deletions

View File

@@ -45,19 +45,10 @@ def discover_snapshots(
host_root = resolve_host_root(global_config.backup_root, host_config.host)
for kind in kinds:
for snapshot_dir in iter_snapshot_dirs(host_root, kind):
meta = read_snapshot_meta(snapshot_dir)
defaults = {
"path": str(snapshot_dir),
"status": str(meta.get("status") or ""),
"started_at": parse_snapshot_datetime(snapshot_dir.name, meta, "started_at"),
"ended_at": parse_snapshot_datetime(snapshot_dir.name, meta, "ended_at"),
"metadata": meta,
}
_record, was_created = SnapshotRecord.objects.update_or_create(
_record, was_created = upsert_snapshot_record(
host=host_config,
kind=kind,
dirname=snapshot_dir.name,
defaults=defaults,
snapshot_dir=snapshot_dir,
)
scanned += 1
if was_created:
@@ -73,6 +64,34 @@ def discover_snapshots(
}
def upsert_snapshot_record(*, host: HostConfig, kind: str, snapshot_dir: Path) -> tuple[SnapshotRecord, bool]:
meta = read_snapshot_meta(snapshot_dir)
defaults = {
"path": str(snapshot_dir),
"status": str(meta.get("status") or ""),
"started_at": parse_snapshot_datetime(snapshot_dir.name, meta, "started_at"),
"ended_at": parse_snapshot_datetime(snapshot_dir.name, meta, "ended_at"),
"metadata": meta,
}
return SnapshotRecord.objects.update_or_create(
host=host,
kind=kind,
dirname=snapshot_dir.name,
defaults=defaults,
)
def infer_snapshot_kind(snapshot_path: Path) -> str:
parent = snapshot_path.parent.name
if parent == "scheduled":
return "scheduled"
if parent == "manual":
return "manual"
if parent == ".incomplete":
return "incomplete"
raise ValueError(f"Cannot infer snapshot kind from path: {snapshot_path}")
def _parse_iso_z(value: str) -> datetime | None:
try:
if value.endswith("Z"):