Aggiunge backup no-GUI per scheduler
This commit is contained in:
38
README.md
38
README.md
@@ -46,6 +46,44 @@ 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.
|
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.
|
||||||
|
|
||||||
|
## Backup senza GUI per Task Scheduler
|
||||||
|
|
||||||
|
Lo stesso entry point della tray puo' eseguire il backup senza mostrare icone o finestre:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python -m bakrest.tray_app --nogui
|
||||||
|
```
|
||||||
|
|
||||||
|
Per testare senza spegnere il PC:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python -m bakrest.tray_app --nogui --no-shutdown
|
||||||
|
```
|
||||||
|
|
||||||
|
In alternativa si puo' chiamare direttamente il modulo dedicato:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python -m bakrest.backup_nogui
|
||||||
|
```
|
||||||
|
|
||||||
|
Questo percorso e' pensato per Task Scheduler, per esempio su evento di disconnessione o fine sessione. Se il server non e' raggiungibile o il backup fallisce, il comando termina con errore e non spegne il PC.
|
||||||
|
|
||||||
|
Azione Task Scheduler consigliata in sviluppo:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Programma: C:\Python314\python.exe
|
||||||
|
Argomenti: -m bakrest.tray_app --nogui
|
||||||
|
Avvia in: C:\devel\bak&rest
|
||||||
|
```
|
||||||
|
|
||||||
|
Per una prova senza spegnimento:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Programma: C:\Python314\python.exe
|
||||||
|
Argomenti: -m bakrest.tray_app --nogui --no-shutdown
|
||||||
|
Avvia in: C:\devel\bak&rest
|
||||||
|
```
|
||||||
|
|
||||||
## Avvio GUI configurazione
|
## Avvio GUI configurazione
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ La tray app deve:
|
|||||||
|
|
||||||
La tray app e' il componente piu' adatto per eseguire `rsync` e richiedere lo spegnimento, perche' queste sono azioni esplicite dell'utente.
|
La tray app e' il componente piu' adatto per eseguire `rsync` e richiedere lo spegnimento, perche' queste sono azioni esplicite dell'utente.
|
||||||
|
|
||||||
|
La stessa logica di backup deve essere disponibile anche senza interfaccia grafica, tramite opzione `--nogui`, per permettere l'esecuzione da Task Scheduler alla disconnessione o fine sessione. In modalita' no-GUI non devono comparire icone tray o finestre di messaggio: gli errori devono essere registrati nei log e il processo deve restituire un codice di uscita diverso da zero.
|
||||||
|
|
||||||
### GUI CustomTkinter
|
### GUI CustomTkinter
|
||||||
|
|
||||||
La GUI di configurazione deve permettere di:
|
La GUI di configurazione deve permettere di:
|
||||||
@@ -398,6 +400,7 @@ dist/
|
|||||||
BakRestWatchdog.exe
|
BakRestWatchdog.exe
|
||||||
BakRestTray.exe
|
BakRestTray.exe
|
||||||
BakRestConfig.exe
|
BakRestConfig.exe
|
||||||
|
BakRestNoGui.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
## Decisioni aperte
|
## Decisioni aperte
|
||||||
|
|||||||
52
src/bakrest/backup_nogui.py
Normal file
52
src/bakrest/backup_nogui.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
from .backup import run_backup, shutdown_after_backup
|
||||||
|
from .config import load_config
|
||||||
|
from .logging_setup import close_logger, configure_logging
|
||||||
|
|
||||||
|
|
||||||
|
def run_nogui_backup(shutdown: bool = True) -> int:
|
||||||
|
config = load_config()
|
||||||
|
logger = configure_logging(config.storage.logs_dir, "backup-nogui")
|
||||||
|
logger.info("BakRest no-GUI backup started")
|
||||||
|
try:
|
||||||
|
result = run_backup(config, logger)
|
||||||
|
if not result.success:
|
||||||
|
logger.error("BakRest no-GUI backup failed: %s", result.message)
|
||||||
|
return _exit_code_for_error(result.error_code)
|
||||||
|
|
||||||
|
logger.info("BakRest no-GUI backup completed: %s", result.message)
|
||||||
|
if shutdown:
|
||||||
|
shutdown_after_backup(config, logger)
|
||||||
|
else:
|
||||||
|
logger.info("Shutdown skipped by command line")
|
||||||
|
return 0
|
||||||
|
finally:
|
||||||
|
close_logger(logger)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description="Run Bak&Rest backup without GUI")
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-shutdown",
|
||||||
|
action="store_true",
|
||||||
|
help="Run the backup but do not shut down the computer",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
return run_nogui_backup(shutdown=not args.no_shutdown)
|
||||||
|
|
||||||
|
|
||||||
|
def _exit_code_for_error(error_code: str | None) -> int:
|
||||||
|
if error_code == "server_unreachable":
|
||||||
|
return 20
|
||||||
|
if error_code == "rsync_start_failed":
|
||||||
|
return 30
|
||||||
|
if error_code == "rsync_failed":
|
||||||
|
return 31
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
@@ -11,6 +12,7 @@ from PIL import Image, ImageDraw
|
|||||||
import pystray
|
import pystray
|
||||||
|
|
||||||
from .backup import run_backup, shutdown_after_backup
|
from .backup import run_backup, shutdown_after_backup
|
||||||
|
from .backup_nogui import run_nogui_backup
|
||||||
from .config import AppConfig, load_config
|
from .config import AppConfig, load_config
|
||||||
from .logging_setup import configure_logging
|
from .logging_setup import configure_logging
|
||||||
from .registry import ChangeRegistry
|
from .registry import ChangeRegistry
|
||||||
@@ -101,7 +103,22 @@ def _show_error_message(title: str, message: str) -> None:
|
|||||||
root.destroy()
|
root.destroy()
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main(argv: list[str] | None = None) -> int:
|
||||||
|
parser = argparse.ArgumentParser(description="Bak&Rest tray app")
|
||||||
|
parser.add_argument(
|
||||||
|
"--nogui",
|
||||||
|
action="store_true",
|
||||||
|
help="Run backup and shutdown without tray icon",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-shutdown",
|
||||||
|
action="store_true",
|
||||||
|
help="With --nogui, run the backup but do not shut down the computer",
|
||||||
|
)
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
if args.nogui:
|
||||||
|
return run_nogui_backup(shutdown=not args.no_shutdown)
|
||||||
|
|
||||||
config = load_config()
|
config = load_config()
|
||||||
logger = configure_logging(config.storage.logs_dir, "tray")
|
logger = configure_logging(config.storage.logs_dir, "tray")
|
||||||
app = BakRestTrayApp(config, logger)
|
app = BakRestTrayApp(config, logger)
|
||||||
|
|||||||
10
tests/test_backup_nogui.py
Normal file
10
tests/test_backup_nogui.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from bakrest.backup_nogui import _exit_code_for_error
|
||||||
|
|
||||||
|
|
||||||
|
def test_nogui_exit_codes_are_specific() -> None:
|
||||||
|
assert _exit_code_for_error("server_unreachable") == 20
|
||||||
|
assert _exit_code_for_error("rsync_start_failed") == 30
|
||||||
|
assert _exit_code_for_error("rsync_failed") == 31
|
||||||
|
assert _exit_code_for_error(None) == 1
|
||||||
Reference in New Issue
Block a user