## Summary
- Fall back to filesystem measurement when snapshot storage metadata is missing. - Prefer `data/` inside snapshot directories so incomplete snapshot metadata/log files are not counted as backup data. - Add stats and dashboard rendering coverage for incomplete snapshots without recorded storage metadata. ## Tests - .venv/bin/python manage.py test src.pobsync_backend.tests.test_stats_summary src.pobsync_backend.tests.test_views.ViewTests.test_dashboard_host_cards_measure_incomplete_data_without_snapshot_metadata --verbosity 2 - .venv/bin/python manage.py check - git diff --check - .venv/bin/python manage.py test src.pobsync_backend --verbosity 2 Closes #62
This commit is contained in:
@@ -5,7 +5,7 @@ from typing import Any, Iterable
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from pobsync.run_stats import filesystem_capacity
|
||||
from pobsync.run_stats import filesystem_capacity, tree_usage
|
||||
|
||||
from .models import BackupRun, GlobalConfig, HostConfig, SnapshotRecord
|
||||
|
||||
@@ -168,6 +168,12 @@ def _snapshot_summary(snapshot: SnapshotRecord | None) -> dict[str, Any]:
|
||||
stats = metadata.get("stats") if isinstance(metadata.get("stats"), dict) else {}
|
||||
storage = stats.get("storage") if isinstance(stats.get("storage"), dict) else {}
|
||||
snapshot_storage = storage.get("snapshot") if isinstance(storage.get("snapshot"), dict) else {}
|
||||
has_recorded_size = (
|
||||
_int_at(snapshot_storage, "allocated_size_bytes") is not None
|
||||
or _int_at(snapshot_storage, "apparent_size_bytes") is not None
|
||||
)
|
||||
if not has_recorded_size:
|
||||
snapshot_storage = _snapshot_storage_from_filesystem(snapshot)
|
||||
return {
|
||||
"id": snapshot.id,
|
||||
"dirname": snapshot.dirname,
|
||||
@@ -180,6 +186,18 @@ def _snapshot_summary(snapshot: SnapshotRecord | None) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def _snapshot_storage_from_filesystem(snapshot: SnapshotRecord) -> dict[str, Any]:
|
||||
if not snapshot.path:
|
||||
return {}
|
||||
snapshot_path = Path(snapshot.path)
|
||||
data_path = snapshot_path / "data"
|
||||
if snapshot_path.name == "data":
|
||||
return tree_usage(snapshot_path)
|
||||
if data_path.exists():
|
||||
return tree_usage(data_path)
|
||||
return tree_usage(snapshot_path)
|
||||
|
||||
|
||||
def _is_real_run(run: BackupRun) -> bool:
|
||||
result = run.result if isinstance(run.result, dict) else {}
|
||||
if result.get("dry_run") is True:
|
||||
|
||||
Reference in New Issue
Block a user