Files
2022-12-06 15:10:36 +01:00

97 lines
3.1 KiB
Python

from __future__ import annotations
from datetime import timedelta, datetime
from pathlib import Path
from ._core import CommandInterface
from ..pool import Pool
from ..misc import assert_type
from ..time import Time
class ZPool(CommandInterface):
_BINARY = 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
def import_from_directory(cls, pool: Pool, directory: Path) -> None:
"""Search `directory` for `pool` and import it."""
assert_type(pool, Pool, "Can only import Pool objects")
cls._run_command(*cls._Subcommands.import_, str(directory), pool.name)
@classmethod
def export(cls, pool: Pool) -> None:
"""Export the pool."""
assert_type(pool, Pool, "Can only export Pool objects")
cls._run_command(*cls._Subcommands.export, pool.name)
@classmethod
def scrub(
cls,
pool: Pool,
*,
skip_if_recently_scrubbed: bool,
recent_scrub_timedelta: timedelta,
wait_for_finish: bool = True,
) -> None:
"""Scrub the pool.
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.")
cls._scrub_if_necessary(pool, skip_if_recently_scrubbed, recent_scrub_timedelta)
if wait_for_finish:
cls._run_command(*cls._Subcommands.wait, pool.name)
if not cls._is_healthy(pool):
raise IOError(f"Pool {pool.name} is not healthy.")
@classmethod
def _scrub_if_necessary(
cls,
pool: Pool,
skip_if_recently_scrubbed: bool,
recent_scrub_timedelta: timedelta,
) -> None:
"""Scrub the pool. If"""
def do_scrub():
cls._run_command(*cls._Subcommands.scrub, pool.name)
if cls._scrub_in_progress(pool):
return
if not skip_if_recently_scrubbed:
do_scrub()
else:
if not cls._recently_scrubbed(pool, recent_scrub_timedelta):
do_scrub()
@classmethod
def _recently_scrubbed(cls, pool: Pool, recent_scrub_timedelta: timedelta) -> bool:
"""Is the interval after the last scrub smaller than the minimum timedelta?"""
return datetime.now() - cls._last_scrub(pool) < recent_scrub_timedelta
@classmethod
def _scrub_in_progress(cls, pool: Pool) -> bool:
return "scrub in progress" in cls._get_pool_status_output(pool)
@classmethod
def _is_healthy(cls, pool: Pool) -> bool:
return "ONLINE" in cls._get_pool_status_output(pool)
@classmethod
def _get_pool_status_output(cls, pool: Pool) -> str:
"""Return output of `zpool status [pool]`."""
return cls._get_output(*cls._Subcommands.status, pool.name)
@classmethod
def _last_scrub(cls, pool: Pool) -> datetime:
"""Get time of the last scrub."""
return Time.get_last_scrub_from_status_output(cls._get_pool_status_output(pool))