Controlla raggiungibilita share backup

This commit is contained in:
2026-07-02 11:07:01 +02:00
parent 169ede14dc
commit afe08a89cb
6 changed files with 82 additions and 6 deletions

View File

@@ -10,7 +10,7 @@ from pathlib import Path
from .config import AppConfig
from .registry import ChangeEvent, ChangeRegistry
from .server_check import check_server
from .server_check import check_backup_target
@dataclass(frozen=True)
@@ -23,7 +23,7 @@ class BackupResult:
def run_backup(config: AppConfig, logger: logging.Logger) -> BackupResult:
status = check_server(config.backup)
status = check_backup_target(config.backup)
if not status.reachable:
message = f"Server backup non raggiungibile: {status.message}"
logger.error(message)

View File

@@ -2,6 +2,7 @@ from __future__ import annotations
import json
import socket
import subprocess
from dataclasses import asdict, dataclass
from datetime import datetime, timezone
from pathlib import Path
@@ -31,6 +32,49 @@ def check_server(config: BackupConfig) -> ServerStatus:
return ServerStatus(False, checked_at, str(exc))
def check_backup_target(config: BackupConfig) -> ServerStatus:
if config.engine != "robocopy":
return check_server(config)
checked_at = datetime.now(timezone.utc).isoformat()
if not config.remote_destinations:
return ServerStatus(False, checked_at, "No backup shares configured")
server_status = check_server(config)
if not server_status.reachable:
return server_status
unreachable = [
destination
for destination in config.remote_destinations
if not share_is_reachable(destination, config.server_check.timeout_seconds)
]
if unreachable:
return ServerStatus(
False,
checked_at,
"Backup share unreachable: " + ", ".join(unreachable),
)
return ServerStatus(True, checked_at, "Backup shares reachable")
def share_is_reachable(destination: str, timeout_seconds: int) -> bool:
probe = destination.rstrip("\\/") + "\\."
command = ["cmd.exe", "/d", "/c", f'if exist "{probe}" (exit /b 0) else (exit /b 1)']
try:
completed = subprocess.run(
command,
capture_output=True,
text=True,
timeout=max(timeout_seconds, 1),
check=False,
)
except (OSError, subprocess.TimeoutExpired):
return False
return completed.returncode == 0
def write_status(path: Path, status: ServerStatus, pending_count: int) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
payload = asdict(status)

View File

@@ -9,7 +9,7 @@ from tkinter import messagebox
from .config import load_config
from .logging_setup import close_logger, configure_logging
from .registry import ChangeRegistry
from .server_check import ServerStatus, check_server, write_status
from .server_check import ServerStatus, check_backup_target, write_status
ALERT_MESSAGE = "Accendi il server di backup o verifica che sia connesso alla rete"
@@ -19,7 +19,7 @@ def run_server_notifier(always_alert: bool = False) -> int:
config = load_config()
logger = configure_logging(config.storage.logs_dir, "server-notifier")
try:
status = check_server(config.backup)
status = check_backup_target(config.backup)
pending_count = ChangeRegistry(config.storage.change_registry).pending_count()
write_status(config.storage.status_file, status, pending_count)

View File

@@ -39,7 +39,7 @@
<Exec>
<Command>C:\Python314\python.exe</Command>
<Arguments>-m bakrest.tray_app --nogui</Arguments>
<WorkingDirectory>C:\devel\bak&amp;rest</WorkingDirectory>
<WorkingDirectory>C:\programmi\bak-and-rest</WorkingDirectory>
</Exec>
</Actions>
</Task>

View File

@@ -50,7 +50,7 @@
<Exec>
<Command>C:\Python314\python.exe</Command>
<Arguments>-m bakrest.server_notifier</Arguments>
<WorkingDirectory>C:\devel\bak&amp;rest</WorkingDirectory>
<WorkingDirectory>C:\programmi\bak-and-rest</WorkingDirectory>
</Exec>
</Actions>
</Task>

View File

@@ -0,0 +1,32 @@
from __future__ import annotations
import subprocess
from bakrest.server_check import share_is_reachable
def test_share_is_reachable_returns_true_on_zero_exit(monkeypatch) -> None:
def fake_run(*args, **kwargs):
return subprocess.CompletedProcess(args[0], 0)
monkeypatch.setattr("bakrest.server_check.subprocess.run", fake_run)
assert share_is_reachable("\\\\server\\share", 3)
def test_share_is_reachable_returns_false_on_nonzero_exit(monkeypatch) -> None:
def fake_run(*args, **kwargs):
return subprocess.CompletedProcess(args[0], 1)
monkeypatch.setattr("bakrest.server_check.subprocess.run", fake_run)
assert not share_is_reachable("\\\\server\\share", 3)
def test_share_is_reachable_returns_false_on_timeout(monkeypatch) -> None:
def fake_run(*args, **kwargs):
raise subprocess.TimeoutExpired(args[0], 3)
monkeypatch.setattr("bakrest.server_check.subprocess.run", fake_run)
assert not share_is_reachable("\\\\server\\share", 3)