# sld-filebackups-py Utility di backup in Python, leggera e senza dipendenze esterne, che archivia file e cartelle dichiarati in un file JSON con rotazione automatica dei backup più vecchi. Pensata per girare come cron job giornaliero su server Linux. --- ## Indice - [Funzionalità](#funzionalità) - [Struttura del progetto](#struttura-del-progetto) - [Come funziona](#come-funziona) - [Installazione](#installazione) - [Configurazione](#configurazione) - [config.json](#configjson) - [dir_backups.json](#dir_backupsjson) - [Ambiente (init.py)](#ambiente-initpy) - [Utilizzo](#utilizzo) - [Struttura dei backup](#struttura-dei-backup) - [Rotazione automatica](#rotazione-automatica) - [Logging](#logging) - [Esecuzione come cron job](#esecuzione-come-cron-job) - [Requisiti](#requisiti) - [Licenza](#licenza) --- ## Funzionalità - **Backup selettivo** — definisci i percorsi da salvare in un file JSON, con un flag di abilitazione per ogni voce; non è necessario toccare il codice per aggiungere o rimuovere entry - **Backup di cartelle** — le directory vengono archiviate come `.tar.gz` (solo il nome della cartella viene preservato come radice dell'archivio, nessun percorso assoluto esposto) - **Backup di file singoli** — i file vengono compressi come `.gz` - **Skip automatico se già esiste** — se un backup per la data odierna è già presente, viene saltato; il script può essere lanciato più volte al giorno senza duplicati - **Rotazione automatica** — dopo ogni esecuzione, gli archivi più vecchi oltre una soglia configurabile vengono eliminati per ogni sottocartella - **Modalità dry-run** — anteprima precisa di cosa verrebbe eliminato dalla rotazione, senza cancellare nulla - **Logging strutturato** — output sempre presente su console (utile per leggere l'output di cron); opzionalmente scrive su file di log persistente - **Supporto multi-ambiente** — switch tra configurazioni `local`, `local2` e `prod` in un unico file - **Gestione degli errori robusta** — entry JSON malformate, percorsi mancanti, cartelle vuote ed errori di permessi vengono catturati e loggati senza interrompere l'intera esecuzione --- ## Struttura del progetto ``` backups_script/ ├── script.py # Punto di ingresso e parser degli argomenti CLI ├── functions.py # Logica principale: backup, rotazione, controlli ├── constants.py # Stato condiviso: percorsi, config caricata, timestamp ├── logger.py # Setup del logging (console + file handler opzionale) ├── init.py # Selettore di ambiente (local / prod) ├── config.json # Configurazione runtime ├── dir_backups.json # Lista dichiarativa dei percorsi da salvare └── LICENSE # GNU GPL v3 ``` ### Responsabilità dei moduli | File | Ruolo | |---|---| | `init.py` | Definisce `ROOT_DIR_APP` e `ROOT_DIR_BACKUPS` in base all'ambiente selezionato. Importato per primo da tutto il resto. | | `constants.py` | Costruisce tutti i percorsi derivati (cartella backup, percorsi config), carica `config.json` e `dir_backups.json` in memoria, cattura la data odierna e l'ora corrente. | | `logger.py` | Legge `config.json` direttamente e configura il logger root di Python con uno `StreamHandler` (sempre attivo) e un `FileHandler` opzionale. | | `functions.py` | Contiene tutta la logica di business: `default_backup_dir()`, `check_existing_folders()`, `backups_now()`, `autorotate_backups()`, `show_enabled()`. | | `script.py` | Inizializza il logging, poi analizza gli argomenti CLI e chiama la funzione appropriata. Senza flag, esegue backup completo + rotazione. | --- ## Come funziona 1. `script.py` chiama `setup_logger()`, che legge `config.json` e configura il logging. 2. `default_backup_dir()` verifica che la cartella di backup root e la sottocartella con il nome host esistano, creandole se necessario. 3. `check_existing_folders()` legge `dir_backups.json`, filtra le entry abilitate (`flag == 1`), verifica che ogni percorso esista su disco e lo classifica come `"folder"` o `"file"`. Le directory vuote o non leggibili vengono escluse. 4. `backups_now()` itera i percorsi verificati: - Per le **cartelle**: crea un archivio `_YYYY-MM-DD.tar.gz` tramite il modulo `tarfile`. - Per i **file singoli**: crea una copia compressa `_YYYY-MM-DD.gz` tramite `gzip` + `shutil.copyfileobj`. - Se l'archivio di oggi esiste già, l'entry viene saltata. 5. `autorotate_backups()` scansiona ogni sottocartella diretta della directory di backup dell'host, ordina i file `.gz` per data di modifica (più recenti prima) ed elimina quelli oltre la soglia `keep_backups`. --- ## Installazione Nessun pacchetto da installare. Il script usa solo la libreria standard di Python. ```bash git clone https://gitea.sld-server.org/sld-admin/sld-filebackups-py.git cd sld-filebackups-py ``` Poi imposta il tuo ambiente e i percorsi in `init.py` e `dir_backups.json`. --- ## Configurazione ### `config.json` ```json { "keep_backups": 7, "logs": false, "logs_path": "/home/backups/logs" } ``` | Chiave | Tipo | Default | Descrizione | |---|---|---|---| | `keep_backups` | intero | `7` | Quanti archivi recenti conservare per sottocartella. I più vecchi vengono eliminati dalla rotazione. | | `logs` | booleano | `false` | Se `true`, viene scritto un file `backup.log` in `logs_path` oltre all'output su console. | | `logs_path` | stringa | `~/backups/logs` | Cartella dove verrà creato `backup.log`. Viene creata automaticamente se non esiste. | > **Nota:** Anche quando `logs` è `false`, tutto l'output viene comunque stampato su stdout/stderr, quindi cron lo cattura normalmente tramite mail o redirezione. --- ### `dir_backups.json` È la lista dichiarativa di tutto ciò che deve essere salvato. Ogni entry è un array JSON di esattamente tre valori: ```json [ [ "/percorso/assoluto/cartella", 1, "NomeBackup" ], [ "/percorso/assoluto/file", 1, "BackupConfig" ], [ "/percorso/disabilitato", 0, "VecchiaEntry" ] ] ``` | Posizione | Campo | Descrizione | |---|---|---| | 0 | `percorso` | Percorso assoluto del file o della cartella da salvare. | | 1 | `abilitato` | `1` = includi nelle esecuzioni di backup. `0` = salta completamente (la entry viene letta ma mai elaborata). | | 2 | `nome` | Identificativo breve usato come nome della sottocartella nella destinazione del backup e come prefisso del nome dell'archivio. Deve essere unico tra le entry. | **Suggerimenti:** - Per disabilitare temporaneamente una entry senza eliminarla, imposta il flag a `0`. - Il campo `nome` diventa una directory dentro `//`, quindi evita spazi e caratteri speciali. - Le cartelle vengono salvate solo se non sono vuote e sono leggibili. --- ### Ambiente (`init.py`) ```python env = "local" # Valori disponibili: "local", "local2", "prod" ``` | Ambiente | `ROOT_DIR_APP` | `ROOT_DIR_BACKUPS` | |---|---|---| | `local` | `/home/sld-admin/Scrivania/backups_script/` | `/backups/Daily_File_Backups/` | | `local2` | `/home/simo-positive/Desktop/backups_script/` | `/backups/Daily_File_Backups/` | | `prod` | `/opt/sld-backups/` | `/home/backups/backups_root/Daily_File_Backups/` | Se viene impostato un valore sconosciuto, il script termina immediatamente con un errore. --- ## Utilizzo ```bash # Backup completo + rotazione automatica (comportamento di default, nessun flag richiesto) python3 script.py # Mostra quali percorsi sono abilitati e quali disabilitati python3 script.py --show # Verifica se i percorsi dichiarati esistono su disco e stampa un report python3 script.py --check # Esegui il backup con output di debug verboso python3 script.py --debug # Esegui solo la rotazione (nessun nuovo backup creato) python3 script.py --rotate # Anteprima di cosa verrebbe eliminato dalla rotazione, senza cancellare nulla python3 script.py --rotate --dry ``` ### Riferimento flag CLI | Flag | Forma lunga | Descrizione | |---|---|---| | `-s` | `--show` | Stampa i percorsi abilitati e disabilitati da `dir_backups.json`. | | `-d` | `--debug` | Esegue il backup con `debug="on"`, abilitando output verboso sul controllo dei percorsi. | | `-c` | `--check` | Esegue `check_existing_folders()` e stampa lo stato dettagliato per ogni percorso dichiarato. | | `-r` | `--rotate` | Esegue solo `autorotate_backups()`. Può essere combinato con `--dry`. | | | `--dry` | Modalità dry-run per `--rotate`: logga i candidati all'eliminazione ma non cancella nulla. | --- ## Struttura dei backup I backup vengono scritti sotto: ``` / └── / ├── Documenti/ │ ├── Documenti_2026-03-10.tar.gz │ ├── Documenti_2026-03-11.tar.gz │ └── Documenti_2026-03-12.tar.gz └── BackupConfig/ ├── BackupConfig_2026-03-10.gz └── BackupConfig_2026-03-11.gz ``` - Ogni entry in `dir_backups.json` ottiene la propria sottocartella con il nome del campo `nome`. - Gli archivi seguono il pattern `_YYYY-MM-DD.tar.gz` (cartelle) o `_YYYY-MM-DD.gz` (file). - Il nome host della macchina viene usato come cartella di primo livello, rendendo semplice raccogliere backup di più macchine sotto la stessa root. --- ## Rotazione automatica La rotazione (`autorotate_backups`) viene eseguita automaticamente dopo ogni backup, oppure può essere avviata manualmente con `--rotate`. **Logica:** 1. Scansiona ogni sottocartella diretta di `//`. 2. Trova tutti i file `*.gz` (copre sia `.gz` che `.tar.gz`). 3. Li ordina per data di modifica, dal più recente al più vecchio. 4. Conserva i primi `keep_backups` (default: 7) ed elimina i restanti. **Dry-run** (`--rotate --dry`) logga esattamente quali file verrebbero eliminati, senza nessuna modifica al filesystem. Utile per verificare l'impostazione di retention prima di applicarla. --- ## Logging Tutte le funzioni usano il modulo standard `logging` di Python tramite un logger con nome (`__name__`). Il logger root viene configurato da `logger.py` all'avvio. - **Output su console** sempre attivo (via `StreamHandler`), indipendentemente dall'impostazione `logs`. - **Output su file** aggiunto quando `"logs": true` è impostato in `config.json`. Il file di log è `/backup.log` e viene aggiunto ad ogni esecuzione. - Formato log: `YYYY-MM-DD HH:MM:SS [LIVELLO] messaggio` --- ## Esecuzione come cron job Per eseguire un backup completo ogni giorno alle 2:00: ```bash crontab -e ``` ``` 0 2 * * * /usr/bin/python3 /opt/sld-backups/script.py >> /home/backups/logs/cron.log 2>&1 ``` Poiché il script scrive sempre su stdout, la redirezione dell'output di cron cattura il log completo dell'esecuzione anche se il log su file è disabilitato in `config.json`. --- ## Requisiti - Python **3.6+** - **Nessun pacchetto di terze parti** — usa solo la libreria standard: - `tarfile`, `gzip`, `shutil` — archiviazione e compressione - `logging` — output strutturato - `argparse` — parsing degli argomenti CLI - `pathlib` — gestione dei percorsi - `socket` — rilevamento del nome host - `json` — caricamento della configurazione --- ## Licenza GNU General Public License v3.0 — vedi [LICENSE](LICENSE) per i termini completi.