This commit is contained in:
timeshifter
2022-12-06 15:10:36 +01:00
parent 695c4fc924
commit 28963c3e64
11 changed files with 58 additions and 41 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ def main():
verify_running_as_root() verify_running_as_root()
local_pool = Pool(config.local_pool_to_backup) local_pool = Pool(config.local_pool_to_backup)
external_pool = ExternalPool.find_from_dict(config.backup_pools) external_pool = ExternalPool.find_from_dict(config.backup_pools)
manager = Manager(local_pool, external_pool) manager = Manager(local_pool, external_pool, config.recent_scrub_timedelta)
manager.backup() manager.backup()
View File
@@ -3,7 +3,7 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from subprocess import Popen, call, check_output, PIPE from subprocess import Popen, call, check_output, PIPE
from misc import assert_type from zfs_backup.misc import assert_type
class CommandInterface: class CommandInterface:
@@ -2,9 +2,9 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from .messages import MAPPER_ENTRY_ALREADY_EXISTS, CANNOT_FIND_DEVICE_PATH from ._core import CommandInterface
from .commands import CommandInterface from ..messages import MAPPER_ENTRY_ALREADY_EXISTS, CANNOT_FIND_DEVICE_PATH
from .misc import DISK_BY_UUID, MAPPER_PATH from ..misc import MAPPER_PATH, DISK_BY_UUID
class Cryptsetup(CommandInterface): class Cryptsetup(CommandInterface):
@@ -3,14 +3,13 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from subprocess import Popen from subprocess import Popen
import config from .. import Pool
from .messages import DELETE_IN_LOCAL_POOL, SAME_DATASET from ._core import CommandInterface
from .misc import assert_type from ..dataset import Dataset
from .commands import CommandInterface from ..messages import SAME_DATASET
from .snapshot import Snapshot from ..misc import assert_type
from .dataset import Dataset from ..snapshot import Snapshot
from .pool import Pool from ..zfs_path import ZFSPath
from .zfs_path import ZFSPath
class ZFS(CommandInterface): class ZFS(CommandInterface):
@@ -65,8 +64,6 @@ class ZFS(CommandInterface):
def destroy_snapshot(cls, snapshot: Snapshot) -> None: def destroy_snapshot(cls, snapshot: Snapshot) -> None:
"""Non-recursively destroy a snapshot.""" """Non-recursively destroy a snapshot."""
assert_type(snapshot, Snapshot, "Can only destroy snapshots.") assert_type(snapshot, Snapshot, "Can only destroy snapshots.")
if config.local_pool_to_backup == snapshot.pool:
raise Exception(DELETE_IN_LOCAL_POOL)
cls._run_command(*cls._Subcommands.destroy, str(snapshot)) cls._run_command(*cls._Subcommands.destroy, str(snapshot))
@classmethod @classmethod
@@ -1,12 +1,12 @@
from __future__ import annotations from __future__ import annotations
from datetime import datetime, timedelta from datetime import timedelta, datetime
from pathlib import Path from pathlib import Path
from .misc import assert_type from ._core import CommandInterface
from .commands import CommandInterface from ..pool import Pool
from .pool import Pool from ..misc import assert_type
from .time import Time from ..time import Time
class ZPool(CommandInterface): class ZPool(CommandInterface):
@@ -33,8 +33,9 @@ class ZPool(CommandInterface):
assert_type(pool, Pool, "Can only export Pool objects") assert_type(pool, Pool, "Can only export Pool objects")
cls._run_command(*cls._Subcommands.export, pool.name) cls._run_command(*cls._Subcommands.export, pool.name)
@classmethod
def scrub( def scrub(
self, cls,
pool: Pool, pool: Pool,
*, *,
skip_if_recently_scrubbed: bool, skip_if_recently_scrubbed: bool,
@@ -45,16 +46,15 @@ class ZPool(CommandInterface):
Waits for the scrub to finish. Raise an IOError if the pool reports as not healthy after the scrub.""" Waits for the scrub to finish. Raise an IOError if the pool reports as not healthy after the scrub."""
assert_type(pool, Pool, "Can only scrub pools.") assert_type(pool, Pool, "Can only scrub pools.")
self._scrub_if_necessary( cls._scrub_if_necessary(pool, skip_if_recently_scrubbed, recent_scrub_timedelta)
pool, skip_if_recently_scrubbed, recent_scrub_timedelta
)
if wait_for_finish: if wait_for_finish:
self._run_command(*self._Subcommands.wait, pool.name) cls._run_command(*cls._Subcommands.wait, pool.name)
if not self._is_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
def _scrub_if_necessary( def _scrub_if_necessary(
self, cls,
pool: Pool, pool: Pool,
skip_if_recently_scrubbed: bool, skip_if_recently_scrubbed: bool,
recent_scrub_timedelta: timedelta, recent_scrub_timedelta: timedelta,
@@ -62,14 +62,14 @@ class ZPool(CommandInterface):
"""Scrub the pool. If""" """Scrub the pool. If"""
def do_scrub(): def do_scrub():
self._run_command(*self._Subcommands.scrub, pool.name) cls._run_command(*cls._Subcommands.scrub, pool.name)
if self._scrub_in_progress(pool): if cls._scrub_in_progress(pool):
return return
if not skip_if_recently_scrubbed: if not skip_if_recently_scrubbed:
do_scrub() do_scrub()
else: else:
if not self._recently_scrubbed(pool, recent_scrub_timedelta): if not cls._recently_scrubbed(pool, recent_scrub_timedelta):
do_scrub() do_scrub()
@classmethod @classmethod
+17 -4
View File
@@ -1,18 +1,25 @@
from __future__ import annotations from __future__ import annotations
from datetime import timedelta
from subprocess import Popen from subprocess import Popen
import config import config
from dataset import Dataset from dataset import Dataset
from pool import Pool, ExternalPool from pool import Pool, ExternalPool
from snapshot import Snapshot from snapshot import Snapshot
from zfs import ZFS from zfs_backup.commands.zfs import ZFS
class Manager: class Manager:
"""Controls the overall backup process.""" """Controls the overall backup process."""
def __init__(self, local_pool: Pool, external_pool: ExternalPool): def __init__(
self,
local_pool: Pool,
external_pool: ExternalPool,
recent_scrub_timedelta: timedelta,
):
self._recent_scrub_timedelta = recent_scrub_timedelta
self._local_pool = local_pool self._local_pool = local_pool
self._external_pool = external_pool self._external_pool = external_pool
@@ -20,10 +27,16 @@ class Manager:
"""Backup workflow.""" """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,
recent_scrub_timedelta=self._recent_scrub_timedelta,
)
self._backup_all_datasets() self._backup_all_datasets()
self._external_pool.clean_old_snapshots() self._external_pool.clean_old_snapshots()
self._external_pool.scrub(skip_if_recently_scrubbed=False) self._external_pool.scrub(
skip_if_recently_scrubbed=False,
recent_scrub_timedelta=self._recent_scrub_timedelta,
)
finally: finally:
self._external_pool.export() self._external_pool.export()
+1 -1
View File
@@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
import config import config
from zfs import ZFS from zfs_backup.commands.zfs import ZFS
from zfs_backup.core import _get_regex_matching_snapshots_with_tags from zfs_backup.core import _get_regex_matching_snapshots_with_tags
from snapshot import Snapshot from snapshot import Snapshot
from zfs_path import ZFSPath from zfs_path import ZFSPath
+1 -1
View File
@@ -4,7 +4,7 @@ from pathlib import Path
from uuid import UUID from uuid import UUID
from .messages import ALREADY_DECRYPTED, NOT_DECRYPTED from .messages import ALREADY_DECRYPTED, NOT_DECRYPTED
from .cryptsetup import Cryptsetup from .commands.cryptsetup import Cryptsetup
from .misc import DISK_BY_UUID from .misc import DISK_BY_UUID
+11 -4
View File
@@ -1,14 +1,15 @@
from __future__ import annotations from __future__ import annotations
from datetime import timedelta
from uuid import UUID from uuid import UUID
from .disk import Disk from .disk import Disk
from .messages import CANNOT_FIND_BACKUP_DRIVE, ALREADY_IMPORTED, NOT_IMPORTED from .messages import CANNOT_FIND_BACKUP_DRIVE, ALREADY_IMPORTED, NOT_IMPORTED
from .misc import MAPPER_PATH, DISK_BY_UUID from .misc import MAPPER_PATH, DISK_BY_UUID
from .zfs import ZFS from .commands.zfs import ZFS
from .dataset import Dataset from .dataset import Dataset
from .zfs_path import ZFSPath from .zfs_path import ZFSPath
from .zpool import ZPool from .commands.zpool import ZPool
class Pool: class Pool:
@@ -52,9 +53,15 @@ class ExternalPool(Pool):
self._imported = False self._imported = False
print(f"Exported {self.name}. Disk {self._disk.uuid} can be removed.") print(f"Exported {self.name}. Disk {self._disk.uuid} can be removed.")
def scrub(self, skip_if_recently_scrubbed: bool) -> None: def scrub(
self, skip_if_recently_scrubbed: bool, recent_scrub_timedelta: timedelta
) -> None:
print(f"Scrubbing {self._name}") print(f"Scrubbing {self._name}")
ZPool.scrub(self, skip_if_recently_scrubbed=skip_if_recently_scrubbed) ZPool.scrub(
self,
skip_if_recently_scrubbed=skip_if_recently_scrubbed,
recent_scrub_timedelta=recent_scrub_timedelta,
)
@classmethod @classmethod
def find_from_dict(cls, backup_pools: dict[str, UUID]) -> ExternalPool: def find_from_dict(cls, backup_pools: dict[str, UUID]) -> ExternalPool:
+1 -1
View File
@@ -3,7 +3,7 @@ from __future__ import annotations
from re import search from re import search
from dataset import Dataset from dataset import Dataset
from zfs import ZFS from zfs_backup.commands.zfs import ZFS
from zfs_path import ZFSPath from zfs_path import ZFSPath