feat(backups): sizing up snapshots

This commit is contained in:
Houkime 2023-02-22 18:48:08 +00:00
parent 48f8f95d83
commit 5d5ceee1cf
4 changed files with 53 additions and 4 deletions

View file

@ -77,3 +77,13 @@ class Backups:
self.restore_service_from_snapshot( self.restore_service_from_snapshot(
get_service_by_id(snapshot.service_name), snapshot.id get_service_by_id(snapshot.service_name), snapshot.id
) )
def service_snapshot_size(self, service: Service, snapshot_id: str) -> float:
repo_name = service.get_id()
return self.provider.backuper.restored_size(repo_name, snapshot_id)
# Our dummy service is not yet globally registered so this is not testable yet
def snapshot_restored_size(self, snapshot: Snapshot) -> float:
return self.service_snapshot_size(
get_service_by_id(snapshot.service_name), snapshot.id
)

View file

@ -25,3 +25,7 @@ class AbstractBackuper(ABC):
def restore_from_backup(self, repo_name: str, snapshot_id: str, folder: str): def restore_from_backup(self, repo_name: str, snapshot_id: str, folder: str):
"""Restore a target folder using a snapshot""" """Restore a target folder using a snapshot"""
raise NotImplementedError raise NotImplementedError
@abstractmethod
def restored_size(self, repo_name, snapshot_id) -> float:
raise NotImplementedError

View file

@ -90,6 +90,25 @@ class ResticBackuper(AbstractBackuper):
if not "created restic repository" in output: if not "created restic repository" in output:
raise ValueError("cannot init a repo: " + output) raise ValueError("cannot init a repo: " + output)
def restored_size(self, repo_name, snapshot_id) -> float:
"""
Size of a snapshot
"""
command = self.restic_command(
repo_name,
"stats",
snapshot_id,
"--json",
)
with subprocess.Popen(command, stdout=subprocess.PIPE, shell=False) as handle:
output = handle.communicate()[0].decode("utf-8")
try:
parsed_output = self.parse_json_output(output)
return parsed_output["total_size"]
except ValueError as e:
raise ValueError("cannot restore a snapshot: " + output) from e
def restore_from_backup(self, repo_name, snapshot_id, folder): def restore_from_backup(self, repo_name, snapshot_id, folder):
""" """
Restore from backup with restic Restore from backup with restic
@ -135,7 +154,7 @@ class ResticBackuper(AbstractBackuper):
if "Is there a repository at the following location?" in output: if "Is there a repository at the following location?" in output:
raise ValueError("No repository! : " + output) raise ValueError("No repository! : " + output)
try: try:
return self.parse_snapshot_output(output) return self.parse_json_output(output)
except ValueError as e: except ValueError as e:
raise ValueError("Cannot load snapshots: ") from e raise ValueError("Cannot load snapshots: ") from e
@ -152,10 +171,18 @@ class ResticBackuper(AbstractBackuper):
snapshots.append(snapshot) snapshots.append(snapshot)
return snapshots return snapshots
def parse_snapshot_output(self, output: str) -> object: def parse_json_output(self, output: str) -> object:
if "[" not in output: indices = [
output.find("["),
output.find("{"),
]
indices = [x for x in indices if x != -1]
if indices == []:
raise ValueError( raise ValueError(
"There is no json in the restic snapshot output : " + output "There is no json in the restic snapshot output : " + output
) )
starting_index = output.find("[")
starting_index = min(indices)
return json.loads(output[starting_index:]) return json.loads(output[starting_index:])

View file

@ -127,3 +127,11 @@ def test_restore(backups, dummy_service):
backups.restore_service_from_snapshot(dummy_service, snap.id) backups.restore_service_from_snapshot(dummy_service, snap.id)
assert path.exists(path_to_nuke) assert path.exists(path_to_nuke)
def test_sizing(backups, dummy_service):
backups.back_up(dummy_service)
snap = backups.get_snapshots(dummy_service)[0]
size = backups.service_snapshot_size(dummy_service, snap.id)
assert size is not None
assert size > 0