refactoring

This commit is contained in:
timeshifter
2021-12-03 20:23:49 +01:00
parent 92bc8d388c
commit 7ada92bb32
+44 -30
View File
@@ -30,6 +30,7 @@ SAME_DATASET = "Cannot send incremental snapshots if start and end snapshot are
DISK_BY_UUID = Path("/dev/disk/by-uuid") DISK_BY_UUID = Path("/dev/disk/by-uuid")
MAPPER_PATH = Path("/dev/mapper") MAPPER_PATH = Path("/dev/mapper")
# noinspection SpellCheckingInspection
""" """
Erstes Backup muss für alle notwendigen Datasets durchgeführt werden mit Erstes Backup muss für alle notwendigen Datasets durchgeführt werden mit
# zfs send -Rv rpool/ROOT@zfs-auto-snap_daily-2018-06-14-1648 | zfs receive -dvF backup1 # zfs send -Rv rpool/ROOT@zfs-auto-snap_daily-2018-06-14-1648 | zfs receive -dvF backup1
@@ -119,7 +120,7 @@ class Manager:
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))
for remote_snap in list(cls._find_snapshots_with_backup_tag(remote_dataset)) for remote_snap in list(cls._find_snapshots_with_backup_tag(remote_dataset))
if local_snap.snapname == remote_snap.snapname if local_snap.snapshot_name == remote_snap.snapshot_name
] ]
common_snapshots.sort() common_snapshots.sort()
return common_snapshots[-1] return common_snapshots[-1]
@@ -131,38 +132,38 @@ def _get_regex_matching_snapshots_with_tags(tags: list[str]):
class ZFSPath: class ZFSPath:
def __init__(self, elements: list[str], snapname: str | None = None): def __init__(self, elements: list[str], snapshot_name: str | None = None):
self._elements = elements self._elements = elements
self._snapname = snapname self._snapshot_name = snapshot_name
self._is_snapshot = False self._is_snapshot = False
if snapname: if snapshot_name:
self._is_snapshot = True self._is_snapshot = True
self._sanity_check() self._sanity_check()
def __repr__(self): def __repr__(self):
path = "/".join(self._elements) path = "/".join(self._elements)
if self._is_snapshot: if self._is_snapshot:
return "@".join([path, self._snapname]) return "@".join([path, self._snapshot_name])
return path return path
def __eq__(self, other: ZFSPath): def __eq__(self, other: ZFSPath):
return ( return (
self._elements == other._elements self._elements == other._elements
and self._is_snapshot == other._is_snapshot and self._is_snapshot == other._is_snapshot
and self._snapname == other._snapname and self._snapshot_name == other._snapshot_name
) )
@classmethod @classmethod
def from_string(cls, string: str) -> ZFSPath: def from_string(cls, string: str) -> ZFSPath:
snapname = None snapshot_name = None
if "@" in string: if "@" in string:
string, snapname = string.split("@") string, snapshot_name = string.split("@")
elements: list[str] = string.split("/") elements: list[str] = string.split("/")
return ZFSPath(elements, snapname) return ZFSPath(elements, snapshot_name)
@property @property
def snapname(self) -> str: def snapshot_name(self) -> str:
return self._snapname return self._snapshot_name
@property @property
def is_snapshot(self) -> bool: def is_snapshot(self) -> bool:
@@ -177,13 +178,13 @@ class ZFSPath:
return ZFSPath(self._elements) return ZFSPath(self._elements)
def replace_pool(self, pool: str) -> ZFSPath: def replace_pool(self, pool: str) -> ZFSPath:
return ZFSPath([pool] + self._elements[1:].copy(), self.snapname) return ZFSPath([pool] + self._elements[1:].copy(), self.snapshot_name)
@property @property
def name_without_pool(self) -> str: def name_without_pool(self) -> str:
name = "/".join(self._elements[1:]) name = "/".join(self._elements[1:])
if self.is_snapshot: if self.is_snapshot:
name = "@".join([name, self.snapname]) name = "@".join([name, self.snapshot_name])
return name return name
def _sanity_check(self): def _sanity_check(self):
@@ -200,17 +201,26 @@ def _assert_type(instance: Any, object_type: Any, message: str) -> None:
class ZPOOL: class ZPOOL:
_ZPOOL = Path("/usr/bin/zpool") _ZPOOL = Path("/usr/bin/zpool")
class _Subcommands:
# -N: no mount
# -d: directory to search the pool in
import_ = ["import", "-N", "-d"]
export = ["export"]
scrub = ["scrub"]
status = ["status"]
wait = ["wait", "-t", "scrub"]
@classmethod @classmethod
def import_from_directory(cls, pool: Pool, directory: Path) -> None: def import_from_directory(cls, pool: Pool, directory: Path) -> None:
_assert_type(pool, Pool, "Can only import Pool objects") _assert_type(pool, Pool, "Can only import Pool objects")
# -N: no mount CommandRunner.run(
# -d: directory to search the pool in [str(cls._ZPOOL), *cls._Subcommands.import_, directory, pool.name]
CommandRunner.run([cls._ZPOOL, "import", "-N", "-d", directory, pool.name]) )
@classmethod @classmethod
def export(cls, pool: Pool) -> None: def export(cls, pool: Pool) -> None:
_assert_type(pool, Pool, "Can only export Pool objects") _assert_type(pool, Pool, "Can only export Pool objects")
CommandRunner.run([cls._ZPOOL, "export", pool.name]) CommandRunner.run([str(cls._ZPOOL), *cls._Subcommands.export, pool.name])
@classmethod @classmethod
def scrub( def scrub(
@@ -223,19 +233,22 @@ class ZPOOL:
_assert_type(pool, Pool, "Can only scrub pools.") _assert_type(pool, Pool, "Can only scrub pools.")
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:
CommandRunner.run([cls._ZPOOL, "wait", "-t", "scrub", pool.name]) CommandRunner.run([str(cls._ZPOOL), *cls._Subcommands.wait, pool.name])
if not cls._healthy(pool): if not cls._healthy(pool):
raise IOError(f"Pool {pool.name} is not healthy.") raise IOError(f"Pool {pool.name} is not healthy.")
@classmethod @classmethod
def _scrub_if_necessary(cls, pool: Pool, skip_if_recently_scrubbed: bool) -> None: def _scrub_if_necessary(cls, pool: Pool, skip_if_recently_scrubbed: bool) -> None:
def do_scrub():
CommandRunner.run([str(cls._ZPOOL), *cls._Subcommands.scrub, pool.name])
if cls._scrub_in_progress(pool): if cls._scrub_in_progress(pool):
return return
if not skip_if_recently_scrubbed: if not skip_if_recently_scrubbed:
CommandRunner.run([cls._ZPOOL, "scrub", pool.name]) do_scrub()
else: else:
if not cls._recently_scrubbed(pool): if not cls._recently_scrubbed(pool):
CommandRunner.run([cls._ZPOOL, "scrub", pool.name]) do_scrub()
@classmethod @classmethod
def _recently_scrubbed(cls, pool: Pool) -> bool: def _recently_scrubbed(cls, pool: Pool) -> bool:
@@ -249,9 +262,9 @@ class ZPOOL:
def _healthy(cls, pool: Pool) -> bool: def _healthy(cls, pool: Pool) -> bool:
return "ONLINE" in cls._get_pool_status_output(pool) return "ONLINE" in cls._get_pool_status_output(pool)
@staticmethod @classmethod
def _get_pool_status_output(pool: Pool) -> str: def _get_pool_status_output(cls, pool: Pool) -> str:
return CommandRunner.get_output([ZFS, "status", pool.name]) return CommandRunner.get_output([ZFS, *cls._Subcommands.status, pool.name])
@classmethod @classmethod
def _last_scrub(cls, pool: Pool) -> datetime: def _last_scrub(cls, pool: Pool) -> datetime:
@@ -276,8 +289,8 @@ class ZFS:
# -R Replicate filesystem # -R Replicate filesystem
# -I send all intermediary snapshots # -I send all intermediary snapshots
send = ["send", "-R", "-I"] send = ["send", "-R", "-I"]
# -d Discard the first element of the sent snapshot's file system name # -d Discard the first element of the "send" snapshot's file system name
# -F Force a rollback of the file system to the most recent snapshot before performing the receive operation. # -F Force a rollback of the file system to the most recent snapshot before performing the "receive".
# -u File system that is associated with the received stream is not mounted. # -u File system that is associated with the received stream is not mounted.
receive = ["receive", "-d", "-F", "-u"] receive = ["receive", "-d", "-F", "-u"]
@@ -411,6 +424,7 @@ class Cryptsetup:
close = "close" close = "close"
status = "status" status = "status"
open = "open" open = "open"
_CRYPTSETUP = Path("/usr/bin/cryptsetup") _CRYPTSETUP = Path("/usr/bin/cryptsetup")
@classmethod @classmethod
@@ -578,14 +592,14 @@ class Snapshot:
return cls(ZFSPath.from_string(qualified_name)) return cls(ZFSPath.from_string(qualified_name))
def __lt__(self, other: Snapshot) -> bool: def __lt__(self, other: Snapshot) -> bool:
return self.snapname < other.snapname return self.snapshot_name < other.snapshot_name
def __repr__(self): def __repr__(self):
return str(self._path) return str(self._path)
@property @property
def snapname(self) -> str: def snapshot_name(self) -> str:
return self._path.snapname return self._path.snapshot_name
def matches_regex(self, regex: str) -> bool: def matches_regex(self, regex: str) -> bool:
return bool(search(regex, str(self._path))) return bool(search(regex, str(self._path)))