Avvisa se server backup non raggiungibile
This commit is contained in:
@@ -46,6 +46,12 @@ python -m bakrest.tray_app
|
||||
|
||||
La tray app legge lo stato prodotto dal servizio e offre il comando `Backup e spegni`. Se il backup fallisce, lo spegnimento non viene eseguito.
|
||||
|
||||
Quando il servizio rileva che il server di backup non e' raggiungibile, la tray app mostra una finestra con:
|
||||
|
||||
```text
|
||||
Accendi il server di backup o verifica che sia connesso alla rete
|
||||
```
|
||||
|
||||
## Backup senza GUI per Task Scheduler
|
||||
|
||||
Lo stesso entry point della tray puo' eseguire il backup senza mostrare icone o finestre:
|
||||
@@ -93,6 +99,7 @@ robocopy_path = "robocopy"
|
||||
[backup.server_check]
|
||||
type = "tcp"
|
||||
port = 445
|
||||
interval_seconds = 600
|
||||
```
|
||||
|
||||
La copia ricrea il percorso relativo sotto ogni share. Se una cartella monitorata e' `D:\Lavori`, il file `D:\Lavori\Cliente\a.psd` viene copiato in:
|
||||
|
||||
@@ -37,6 +37,8 @@ Il servizio Windows deve:
|
||||
- verificare periodicamente la raggiungibilita' del server di backup;
|
||||
- segnalare lo stato del server alla tray app.
|
||||
|
||||
Il controllo di raggiungibilita' del server deve avvenire ogni 10 minuti, salvo diversa configurazione.
|
||||
|
||||
La libreria Python consigliata per il monitoraggio e' `watchdog`, perche' su Windows usa API native di sistema ed evita un polling continuo e inefficiente.
|
||||
|
||||
### Tray app
|
||||
@@ -177,6 +179,12 @@ La tray app deve mostrare chiaramente se il server risulta spento o non raggiung
|
||||
|
||||
La non raggiungibilita' del server deve essere registrata nei log giornalieri.
|
||||
|
||||
Poiche' un servizio Windows non deve aprire direttamente finestre nella sessione utente, la finestra di avviso deve essere mostrata dalla tray app quando legge uno stato server non raggiungibile. Il messaggio richiesto e':
|
||||
|
||||
```text
|
||||
Accendi il server di backup o verifica che sia connesso alla rete
|
||||
```
|
||||
|
||||
## Strategia di monitoraggio
|
||||
|
||||
Non e' consigliato monitorare tutto il disco filtrando solo per estensione, ad esempio `C:\` con filtro `.jpg`, `.png`, `.mp4`. Questo approccio puo' generare molti eventi inutili, includere directory di sistema, rallentare il sistema e creare un registro troppo rumoroso.
|
||||
|
||||
@@ -57,7 +57,7 @@ shutdown_command = "shutdown /s /t 0"
|
||||
[backup.server_check]
|
||||
type = "tcp"
|
||||
port = 445
|
||||
interval_seconds = 300
|
||||
interval_seconds = 600
|
||||
timeout_seconds = 3
|
||||
|
||||
[stability]
|
||||
|
||||
@@ -123,6 +123,9 @@ def migrate_config_data(data: dict[str, Any]) -> bool:
|
||||
if isinstance(server_check, dict) and int(server_check.get("port", 22)) == 22:
|
||||
server_check["port"] = 445
|
||||
changed = True
|
||||
if isinstance(server_check, dict) and int(server_check.get("interval_seconds", 300)) == 300:
|
||||
server_check["interval_seconds"] = 600
|
||||
changed = True
|
||||
|
||||
destinations = backup.get("remote_destinations")
|
||||
if not isinstance(destinations, list):
|
||||
|
||||
@@ -57,7 +57,7 @@ shutdown_command = "shutdown /s /t 0"
|
||||
[backup.server_check]
|
||||
type = "tcp"
|
||||
port = 445
|
||||
interval_seconds = 300
|
||||
interval_seconds = 600
|
||||
timeout_seconds = 3
|
||||
|
||||
[stability]
|
||||
|
||||
@@ -4,6 +4,7 @@ import argparse
|
||||
import json
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
from pathlib import Path
|
||||
@@ -23,6 +24,8 @@ class BakRestTrayApp:
|
||||
self.config = config
|
||||
self.logger = logger
|
||||
self.registry = ChangeRegistry(config.storage.change_registry)
|
||||
self.stop_event = threading.Event()
|
||||
self.last_alert_reachable: bool | None = None
|
||||
self.icon = pystray.Icon(
|
||||
"BakRestTray",
|
||||
_create_icon_image(),
|
||||
@@ -32,6 +35,7 @@ class BakRestTrayApp:
|
||||
|
||||
def run(self) -> None:
|
||||
self.logger.info("BakRestTray started")
|
||||
threading.Thread(target=self._server_status_monitor, daemon=True).start()
|
||||
self.icon.run()
|
||||
|
||||
def _build_menu(self) -> pystray.Menu:
|
||||
@@ -49,14 +53,19 @@ class BakRestTrayApp:
|
||||
return f"Server: {server} | File in attesa: {pending}"
|
||||
|
||||
def _read_server_status(self) -> str:
|
||||
status = self._read_server_status_data()
|
||||
if status is None:
|
||||
return "sconosciuto"
|
||||
return "raggiungibile" if status.get("reachable") else "non raggiungibile"
|
||||
|
||||
def _read_server_status_data(self) -> dict[str, object] | None:
|
||||
path = self.config.storage.status_file
|
||||
if not path.exists():
|
||||
return "sconosciuto"
|
||||
return None
|
||||
try:
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
except (OSError, json.JSONDecodeError):
|
||||
return "errore stato"
|
||||
return "raggiungibile" if data.get("reachable") else "non raggiungibile"
|
||||
return None
|
||||
|
||||
def _backup_and_shutdown(self, icon: pystray.Icon, item: pystray.MenuItem) -> None:
|
||||
threading.Thread(target=self._backup_and_shutdown_worker, daemon=True).start()
|
||||
@@ -81,8 +90,29 @@ class BakRestTrayApp:
|
||||
|
||||
def _quit(self, icon: pystray.Icon, item: pystray.MenuItem) -> None:
|
||||
self.logger.info("BakRestTray stopped")
|
||||
self.stop_event.set()
|
||||
self.icon.stop()
|
||||
|
||||
def _server_status_monitor(self) -> None:
|
||||
interval = min(max(self.config.backup.server_check.interval_seconds, 60), 600)
|
||||
while not self.stop_event.wait(interval):
|
||||
status = self._read_server_status_data()
|
||||
if status is None:
|
||||
continue
|
||||
|
||||
reachable = bool(status.get("reachable"))
|
||||
if reachable:
|
||||
self.last_alert_reachable = True
|
||||
continue
|
||||
|
||||
if self.last_alert_reachable is not False:
|
||||
self.last_alert_reachable = False
|
||||
self.logger.warning("Showing backup server unreachable alert")
|
||||
_show_error_message(
|
||||
"Bak&Rest",
|
||||
"Accendi il server di backup o verifica che sia connesso alla rete",
|
||||
)
|
||||
|
||||
|
||||
def _create_icon_image() -> Image.Image:
|
||||
image = Image.new("RGBA", (64, 64), (26, 38, 52, 255))
|
||||
|
||||
@@ -84,7 +84,11 @@ class WatchdogRuntime:
|
||||
|
||||
def _run_health_loop(self) -> None:
|
||||
interval = self.config.backup.server_check.interval_seconds
|
||||
self._publish_server_status()
|
||||
while not self.stop_event.wait(interval):
|
||||
self._publish_server_status()
|
||||
|
||||
def _publish_server_status(self) -> None:
|
||||
status = check_server(self.config.backup)
|
||||
pending_count = self.registry.pending_count()
|
||||
write_status(self.config.storage.status_file, status, pending_count)
|
||||
|
||||
@@ -66,6 +66,7 @@ def test_migrate_legacy_rsync_config_to_robocopy_defaults() -> None:
|
||||
assert data["backup"]["engine"] == "robocopy"
|
||||
assert data["backup"]["robocopy_path"] == "robocopy"
|
||||
assert data["backup"]["server_check"]["port"] == 445
|
||||
assert data["backup"]["server_check"]["interval_seconds"] == 600
|
||||
assert data["backup"]["remote_destinations"] == []
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user