add: skip scrub if recently scrubbed

This commit is contained in:
timeshifter
2021-12-02 16:20:55 +01:00
parent 359a6a99a8
commit a4e9b12c4c
2 changed files with 64 additions and 10 deletions
+3
View File
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from uuid import UUID from uuid import UUID
from datetime import timedelta
# Name des lokalen Pools # Name des lokalen Pools
local_pool_to_backup = "tank" local_pool_to_backup = "tank"
@@ -16,6 +17,8 @@ do_not_backup = [
"tank/docker", "tank/docker",
] ]
recent_scrub_timedelta = timedelta(hours=24)
# überprüfe ob neue Snapshots mit diesen String-Teilen vorliegen # überprüfe ob neue Snapshots mit diesen String-Teilen vorliegen
snapshot_tag = "znap" snapshot_tag = "znap"
snapshot_interval = "monthly" snapshot_interval = "monthly"
+61 -10
View File
@@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
from datetime import datetime
from functools import cache from functools import cache
from os import getuid from os import getuid
from pathlib import Path from pathlib import Path
@@ -61,10 +62,10 @@ class Manager:
def backup(self): def backup(self):
self._external_pool.import_() self._external_pool.import_()
try: try:
self._external_pool.scrub() self._external_pool.scrub(skip_if_recently_scrubbed=True)
self._backup_all_datasets() self._backup_all_datasets()
self._external_pool.clean_old_snapshots() self._external_pool.clean_old_snapshots()
self._external_pool.scrub() self._external_pool.scrub(skip_if_recently_scrubbed=False)
finally: finally:
self._external_pool.export() self._external_pool.export()
@@ -280,28 +281,78 @@ class ZFS:
) )
@classmethod @classmethod
def scrub(cls, pool: Pool, *, wait_for_finish: bool = True) -> None: def scrub(cls, pool: Pool, *, skip_if_recently_scrubbed: bool, wait_for_finish: bool = True) -> None:
if not isinstance(pool, Pool): if not isinstance(pool, Pool):
raise TypeError("Can only scrub pools.") raise TypeError("Can only scrub pools.")
if not cls._scrub_in_progress(pool): cls._scrub_if_necessary(pool, skip_if_recently_scrubbed)
Command.run([config.ZPOOL, "scrub", pool.name])
if wait_for_finish: if wait_for_finish:
Command.run([config.ZPOOL, "wait", "-t", "scrub", pool.name]) Command.run([config.ZPOOL, "wait", "-t", "scrub", 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
def _scrub_if_necessary(cls, pool: Pool, skip_if_recently_scrubbed: bool) -> None:
if cls._scrub_in_progress(pool):
return
if not skip_if_recently_scrubbed:
Command.run([config.ZPOOL, "scrub", pool.name])
else:
if not cls._recently_scrubbed(pool):
Command.run([config.ZPOOL, "scrub", pool.name])
@classmethod
def _recently_scrubbed(cls, pool):
return datetime.now() - cls._last_scrub(pool) < config.recent_scrub_timedelta
@classmethod @classmethod
def _scrub_in_progress(cls, pool: Pool) -> bool: def _scrub_in_progress(cls, pool: Pool) -> bool:
return "scrub in progress" in cls._pool_status_output(pool) return "scrub in progress" in cls._get_pool_status_output(pool)
@classmethod @classmethod
def _healthy(cls, pool: Pool) -> bool: def _healthy(cls, pool: Pool) -> bool:
return "ONLINE" in cls._pool_status_output(pool) return "ONLINE" in cls._get_pool_status_output(pool)
@staticmethod @staticmethod
def _pool_status_output(pool): def _get_pool_status_output(pool) -> str:
return Command.get_output([config.ZFS, "status", pool.name]) return Command.get_output([config.ZFS, "status", pool.name])
@classmethod
def _last_scrub(cls, pool: Pool) -> datetime:
return Time.get_last_scrub_from_status_output(cls._get_pool_status_output(pool))
class Time:
@staticmethod
def _get_date_format() -> str:
"""date format as used by zpool status
%a: day of week, short version
%b: month, short version
%e: day of month, space-padded
%H, %M, %S: hour, minute, second, zero-padded
%Y: year
"""
return "%a %b %e %H:%M:%S %Y"
@classmethod
def _extract_datetime_segment(cls, line_with_datetime: str) -> str:
return line_with_datetime.split(" errors on ")[-1]
@classmethod
def _get_line_with_datetime_from_status_output(cls, output: str) -> str:
lines = output.split("\n")
for line in lines:
if line.startswith(" scan: scrub repaired"):
return line.strip()
else:
raise ValueError("Could not find the correct line")
@classmethod
def get_last_scrub_from_status_output(cls, output: str) -> datetime:
line_with_datetime = cls._get_line_with_datetime_from_status_output(output)
date_str = cls._extract_datetime_segment(line_with_datetime)
return datetime.strptime(date_str, cls._get_date_format())
class Disk: class Disk:
def __init__(self, uuid: UUID, name: str): def __init__(self, uuid: UUID, name: str):
@@ -380,9 +431,9 @@ class ExternalPool(Pool):
# TODO cancel scrub if necessary # TODO cancel scrub if necessary
ZFS.export_pool(self._name) ZFS.export_pool(self._name)
def scrub(self) -> None: def scrub(self, skip_if_recently_scrubbed: bool) -> None:
print(f"Scrubbing {self._name}") print(f"Scrubbing {self._name}")
ZFS.scrub(self) ZFS.scrub(self, skip_if_recently_scrubbed=skip_if_recently_scrubbed)
@classmethod @classmethod
def find_from_dict(cls, backup_pools: dict[str, UUID]) -> ExternalPool: def find_from_dict(cls, backup_pools: dict[str, UUID]) -> ExternalPool: