add: docstrings in Manager

This commit is contained in:
timeshifter
2021-12-04 23:07:09 +01:00
parent be47ff5f68
commit d22108d20a
+39 -10
View File
@@ -48,6 +48,8 @@ def main():
class Manager: class Manager:
"""Controls the overall backup process."""
_map_type: dict[Dataset, Dataset | None] _map_type: dict[Dataset, Dataset | None]
def __init__(self, local_pool: Pool, external_pool: ExternalPool): def __init__(self, local_pool: Pool, external_pool: ExternalPool):
@@ -55,6 +57,7 @@ class Manager:
self._external_pool = external_pool self._external_pool = external_pool
def backup(self): def backup(self):
"""Backup workflow."""
self._external_pool.import_() self._external_pool.import_()
try: try:
self._external_pool.scrub(skip_if_recently_scrubbed=True) self._external_pool.scrub(skip_if_recently_scrubbed=True)
@@ -65,18 +68,26 @@ class Manager:
self._external_pool.export() self._external_pool.export()
def _backup_all_datasets(self): def _backup_all_datasets(self):
"""Send snapshots of local datasets to the backup pool."""
print("Backing up datasets") print("Backing up datasets")
for local_dataset, remote_dataset in self._get_dataset_backup_map().items(): for (
local_dataset,
remote_dataset,
) in self._get_user_filtered_dataset_backup_map().items():
if remote_dataset is None: if remote_dataset is None:
stream = self._send_absolute(local_dataset) stream = self._send_absolute(local_dataset)
else: else:
stream = self._send_incremental(local_dataset, remote_dataset) stream = self._send_incremental(local_dataset, remote_dataset)
ZFS.receive(stream, self._external_pool) ZFS.receive(stream, self._external_pool)
def _get_dataset_backup_map(self) -> _map_type: def _get_user_filtered_dataset_backup_map(self) -> _map_type:
return self._user_confirm_new_datasets(self._get_raw_dataset_map()) return self._user_confirm_new_datasets(self._get_raw_dataset_map())
def _user_confirm_new_datasets(self, dataset_map: _map_type) -> _map_type: def _user_confirm_new_datasets(self, dataset_map: _map_type) -> _map_type:
"""For datasets that exist only locally, ask the user if he wants to back it up.
If he doesn't, remove it from the dict.
"""
return { return {
local_dataset: remote_dataset local_dataset: remote_dataset
for local_dataset, remote_dataset in dataset_map.items() for local_dataset, remote_dataset in dataset_map.items()
@@ -84,6 +95,10 @@ class Manager:
} }
def _get_raw_dataset_map(self) -> _map_type: def _get_raw_dataset_map(self) -> _map_type:
"""Return map from local to remote datasets.
If the local dataset has no remote match, set the remote dataset to None.
"""
return { return {
local_dataset: self._search_matching_dataset_in_remote_pool( local_dataset: self._search_matching_dataset_in_remote_pool(
local_dataset, self._external_pool local_dataset, self._external_pool
@@ -94,23 +109,29 @@ class Manager:
def _send_incremental( def _send_incremental(
self, local_dataset: Dataset, remote_dataset: Dataset self, local_dataset: Dataset, remote_dataset: Dataset
) -> Popen: ) -> Popen:
start_snapshot = self._get_last_common_snapshot(local_dataset, remote_dataset) """Send incremental stream between two datasets to remote."""
end_snapshot = self._get_last_snapshot_with_backup_tag(local_dataset) start_snapshot = self._get_newest_common_snapshot_with_backup_tags(
local_dataset, remote_dataset
)
end_snapshot = self._get_newest_snapshot_with_backup_tag(local_dataset)
stream = ZFS.send_incremental(start_snapshot, end_snapshot) stream = ZFS.send_incremental(start_snapshot, end_snapshot)
return stream return stream
def _send_absolute(self, local_dataset: Dataset) -> Popen: def _send_absolute(self, local_dataset: Dataset) -> Popen:
snapshot = self._get_last_snapshot_with_backup_tag(local_dataset) """Send absolute stream to remote."""
snapshot = self._get_newest_snapshot_with_backup_tag(local_dataset)
return ZFS.send_absolute(snapshot) return ZFS.send_absolute(snapshot)
def _get_last_snapshot_with_backup_tag(self, local_dataset): def _get_newest_snapshot_with_backup_tag(self, dataset: Dataset) -> Snapshot:
snapshots = self._find_snapshots_with_backup_tag(local_dataset) """From all snapshots in the dataset, get the newest one that matches the backup tags in the config."""
snapshots = self._find_snapshots_with_backup_tag(dataset)
snapshots.sort() snapshots.sort()
end_snapshot = snapshots[-1] end_snapshot = snapshots[-1]
return end_snapshot return end_snapshot
@property @property
def _datasets_to_backup(self) -> list[Dataset]: def _datasets_to_backup(self) -> list[Dataset]:
"""Return local datasets to consider for a backup."""
return [ return [
dataset dataset
for dataset in self._local_pool.datasets for dataset in self._local_pool.datasets
@@ -119,6 +140,7 @@ class Manager:
@staticmethod @staticmethod
def _find_snapshots_with_backup_tag(dataset: Dataset) -> list[Snapshot]: def _find_snapshots_with_backup_tag(dataset: Dataset) -> list[Snapshot]:
"""Find snapshots of a dataset that carry the backup tags."""
regex = _get_regex_matching_snapshots_with_tags( regex = _get_regex_matching_snapshots_with_tags(
[config.snapshot_tag, config.snapshot_interval] [config.snapshot_tag, config.snapshot_interval]
) )
@@ -130,15 +152,22 @@ class Manager:
def _search_matching_dataset_in_remote_pool( def _search_matching_dataset_in_remote_pool(
dataset: Dataset, pool: Pool dataset: Dataset, pool: Pool
) -> Dataset | None: ) -> Dataset | None:
"""For a dataset in the local pool, get the matching dataset in the remote pool.
If there is no match, return None.
"""
dataset_to_search = dataset.replace_pool(pool.name) dataset_to_search = dataset.replace_pool(pool.name)
for dataset in pool.datasets: for dataset in pool.datasets:
if dataset == dataset_to_search: if dataset == dataset_to_search:
return dataset return dataset
@classmethod @classmethod
def _get_last_common_snapshot( def _get_newest_common_snapshot_with_backup_tags(
cls, local_dataset: Dataset, remote_dataset: Dataset cls, local_dataset: Dataset, remote_dataset: Dataset
) -> Snapshot: ) -> Snapshot:
"""In a local and a remote dataset, find the newest snapshot with backup tags which exists in both datasets.
Return the local snapshot."""
common_snapshots = [ common_snapshots = [
local_snap local_snap
for local_snap in list(cls._find_snapshots_with_backup_tag(local_dataset)) for local_snap in list(cls._find_snapshots_with_backup_tag(local_dataset))
@@ -287,7 +316,7 @@ class ZPool(CommandInterface):
cls._scrub_if_necessary(pool, skip_if_recently_scrubbed) cls._scrub_if_necessary(pool, skip_if_recently_scrubbed)
if wait_for_finish: if wait_for_finish:
cls._run_command(*cls._Subcommands.wait, pool.name) cls._run_command(*cls._Subcommands.wait, pool.name)
if not cls._healthy(pool): if not cls._is_healthy(pool):
raise IOError(f"Pool {pool.name} is not healthy.") raise IOError(f"Pool {pool.name} is not healthy.")
@classmethod @classmethod
@@ -312,7 +341,7 @@ class ZPool(CommandInterface):
return "scrub in progress" in cls._get_pool_status_output(pool) return "scrub in progress" in cls._get_pool_status_output(pool)
@classmethod @classmethod
def _healthy(cls, pool: Pool) -> bool: def _is_healthy(cls, pool: Pool) -> bool:
return "ONLINE" in cls._get_pool_status_output(pool) return "ONLINE" in cls._get_pool_status_output(pool)
@classmethod @classmethod