(feature) Capture structured backup statistics
Parse rsync --stats output into structured run metrics for file counts, transferred bytes, literal data, matched data, speedup, and estimated link-dest savings. Store collected stats on backup run results and successful snapshot metadata, including snapshot data usage and backup-root capacity details for future dashboard graphs and disk-full projections. Render the collected metrics on run and snapshot detail pages, with tests covering parsing, metadata persistence, and UI output.
This commit is contained in:
@@ -753,6 +753,18 @@ class ViewTests(TestCase):
|
||||
"prune_max_delete": 10,
|
||||
"prune_protect_bases": False,
|
||||
},
|
||||
"stats": {
|
||||
"duration_seconds": 12,
|
||||
"rsync": {
|
||||
"files_total": 10,
|
||||
"files_transferred": 2,
|
||||
"total_file_size_bytes": 2000,
|
||||
"total_transferred_file_size_bytes": 500,
|
||||
"literal_data_bytes": 500,
|
||||
"matched_data_bytes": 1500,
|
||||
"link_dest_estimated_savings_bytes": 1500,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@@ -766,6 +778,9 @@ class ViewTests(TestCase):
|
||||
self.assertContains(response, "Requested Options")
|
||||
self.assertContains(response, "Dry run:</strong> yes")
|
||||
self.assertContains(response, "Verbose rsync output:</strong> yes")
|
||||
self.assertContains(response, "Stats")
|
||||
self.assertContains(response, "Files seen:</strong> 10")
|
||||
self.assertContains(response, "Estimated link-dest saving")
|
||||
self.assertContains(response, ""ok": true")
|
||||
self.assertContains(response, reverse("snapshot_detail", args=[snapshot.id]))
|
||||
|
||||
@@ -811,7 +826,30 @@ class ViewTests(TestCase):
|
||||
self.client.force_login(self.staff_user)
|
||||
host = HostConfig.objects.create(host="web-01", address="web-01.example.test")
|
||||
base = self._snapshot(host, "20260518-021500Z__BASESNAP")
|
||||
base.metadata = {"status": "success", "snapshot_id": "BASESNAP"}
|
||||
base.metadata = {
|
||||
"status": "success",
|
||||
"snapshot_id": "BASESNAP",
|
||||
"stats": {
|
||||
"duration_seconds": 20,
|
||||
"rsync": {
|
||||
"files_total": 100,
|
||||
"files_transferred": 4,
|
||||
"total_file_size_bytes": 10_000,
|
||||
"link_dest_estimated_savings_bytes": 7_000,
|
||||
},
|
||||
"storage": {
|
||||
"snapshot": {
|
||||
"apparent_size_bytes": 10_000,
|
||||
"allocated_size_bytes": 3_000,
|
||||
"hardlinked_files": 9,
|
||||
},
|
||||
"capacity": {
|
||||
"used_percent": 30.5,
|
||||
"available_bytes": 1_000_000,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
base.save(update_fields=["metadata"])
|
||||
child = self._snapshot(host, "20260519-021500Z__CHILDSNP")
|
||||
child.base = base
|
||||
@@ -824,6 +862,9 @@ class ViewTests(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, base.dirname)
|
||||
self.assertContains(response, "BASESNAP")
|
||||
self.assertContains(response, "Stats")
|
||||
self.assertContains(response, "Files seen:</strong> 100")
|
||||
self.assertContains(response, "Hardlinked files:</strong> 9")
|
||||
self.assertContains(response, child.dirname)
|
||||
self.assertContains(response, f"Run {run.id}")
|
||||
self.assertContains(response, reverse("run_detail", args=[run.id]))
|
||||
|
||||
Reference in New Issue
Block a user