refactor
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from subprocess import Popen
|
||||
|
||||
from .. import Pool
|
||||
from ._core import CommandInterface
|
||||
from ..dataset import Dataset
|
||||
from ..messages import SAME_DATASET
|
||||
from ..misc import assert_type
|
||||
from ..snapshot import Snapshot
|
||||
from ..zfs_path import ZFSPath
|
||||
|
||||
|
||||
class ZFS(CommandInterface):
|
||||
|
||||
"""Interface of `zfs` commands"""
|
||||
|
||||
class _Subcommands:
|
||||
class List:
|
||||
# -H Scripting mode, omit headers
|
||||
# -d1 maximum depth of 1
|
||||
# -o name output column
|
||||
# -t object type
|
||||
# -r recursive
|
||||
_base = ["list", "-H", "-d1", "-o", "name"]
|
||||
dataset = [*_base, "-t", "filesystem"]
|
||||
snapshot = [*_base, "-t", "snapshot", "-r"]
|
||||
|
||||
class Send:
|
||||
# -R Replicate filesystem
|
||||
# -I send all intermediary snapshots
|
||||
absolute = ["send", "-R"]
|
||||
incremental = [*absolute, "-I"]
|
||||
|
||||
destroy = ["destroy"]
|
||||
# -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".
|
||||
# -u File system that is associated with the received stream is not mounted.
|
||||
receive = ["receive", "-d", "-F", "-u"]
|
||||
|
||||
_BINARY = Path("/usr/bin/zfs")
|
||||
|
||||
@classmethod
|
||||
def get_first_level_datasets(cls, pool: Pool) -> list[Dataset]:
|
||||
"""Get the first level datasets of a pool.
|
||||
|
||||
If a pool `p` has the datasets `p/a`, `p/a/b` and `p/c`, return `[p/a, p/c]`."""
|
||||
assert_type(pool, Pool, "Can only get datasets from Pool objects")
|
||||
datasets_str = cls._get_output(*cls._Subcommands.List.dataset, pool.name)
|
||||
# we omit the first return value, which is the pool, not the dataset
|
||||
datasets_list = datasets_str.split("\n")[1:]
|
||||
return [Dataset(ZFSPath.from_string(d)) for d in datasets_list]
|
||||
|
||||
@classmethod
|
||||
def get_snapshots(cls, dataset: Dataset) -> list[Snapshot]:
|
||||
"""Get list of snapshots that are associated with a dataset."""
|
||||
assert_type(dataset, Dataset, "Can only get snapshots from Dataset object")
|
||||
command = [*cls._Subcommands.List.snapshot, dataset.qualified_name]
|
||||
snapshot_names: list[str] = cls._get_output(*command).split("\n")
|
||||
return [Snapshot(ZFSPath.from_string(name)) for name in snapshot_names]
|
||||
|
||||
@classmethod
|
||||
def destroy_snapshot(cls, snapshot: Snapshot) -> None:
|
||||
"""Non-recursively destroy a snapshot."""
|
||||
assert_type(snapshot, Snapshot, "Can only destroy snapshots.")
|
||||
cls._run_command(*cls._Subcommands.destroy, str(snapshot))
|
||||
|
||||
@classmethod
|
||||
def send_incremental(
|
||||
cls, old_snapshot: Snapshot, new_snapshot: Snapshot
|
||||
) -> Popen[str]:
|
||||
"""Send the incremental replicating stream between two snapshots."""
|
||||
cls._pre_send_sanity_checks(old_snapshot, new_snapshot)
|
||||
print(f" {old_snapshot} -> {new_snapshot}")
|
||||
return cls._open_stream(
|
||||
*cls._Subcommands.Send.incremental,
|
||||
str(old_snapshot),
|
||||
str(new_snapshot),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _pre_send_sanity_checks(
|
||||
cls, old_snapshot: Snapshot, new_snapshot: Snapshot
|
||||
) -> None:
|
||||
for snapshot in [old_snapshot, new_snapshot]:
|
||||
assert_type(
|
||||
snapshot,
|
||||
Snapshot,
|
||||
f"Cannot send stream, object is not a Snapshot: {snapshot}",
|
||||
)
|
||||
if old_snapshot.dataset != new_snapshot.dataset:
|
||||
raise ValueError(SAME_DATASET)
|
||||
if old_snapshot.newer_than(new_snapshot):
|
||||
raise ValueError("Old snapshot is newer than new snapshot.")
|
||||
if old_snapshot == new_snapshot:
|
||||
raise ValueError("Cannot send stream, snapshots are identical.")
|
||||
|
||||
@classmethod
|
||||
def receive(cls, stream: Popen[str], pool: Pool):
|
||||
"""Receive a stream generated with `zfs send` into a pool."""
|
||||
assert_type(pool, Pool, "Can only receive into pools.")
|
||||
cls._receive_stream(*cls._Subcommands.receive, pool.name, stream=stream)
|
||||
|
||||
@classmethod
|
||||
def send_absolute(cls, snapshot: Snapshot) -> Popen:
|
||||
"""Send the non-incremental replicating stream of the snapshot."""
|
||||
print(f" {snapshot} -> [new]")
|
||||
return cls._open_stream(*cls._Subcommands.Send.absolute, str(snapshot))
|
||||
Reference in New Issue
Block a user