# πŸ“„ PVE Backup Report ![Version](https://img.shields.io/badge/version-1.0.0-blue) ![Python](https://img.shields.io/badge/python-3.11%2B-green) ![License](https://img.shields.io/badge/license-Apache--2.0-orange) ![Docker](https://img.shields.io/badge/docker-ready-2496ED) Python tool to generate a daily backup report for Proxmox VE against Proxmox Backup Server. The application collects data via the PVE/PBS APIs, computes VM/CT coverage, retrieves the latest known backups and generates a PDF report using WeasyPrint. The PDF includes a cover page, a table of contents, summary KPIs, PBS storages, backup jobs, VM/CT coverage, PBS retention and collection anomalies. ## ✨ Features - πŸ“„ Timestamped daily PDF report, without overwriting previous reports. - 🧭 Inventory of PBS storages declared in Proxmox VE. - βœ… Coverage analysis for QEMU VMs and LXC containers. - πŸ•’ Retrieval of the latest known backups via PVE tasks. - πŸ—„οΈ Optional collection of PBS datastores, namespaces, prune jobs and snapshots. - ⚠️ Anomalies section for errors or partial collection data. - 🐳 Recommended execution with Docker, or direct CLI execution. ## 🐳 Usage with Docker Docker is the recommended execution mode. The image includes the Python dependencies and system libraries required by WeasyPrint. ### πŸ“¦ Setup ```sh cp .env.example .env mkdir -p reports ``` Then edit `.env` with your PVE cluster and PBS connection details. In Docker, keep: ```env REPORT_OUTPUT_DIR=/reports ``` The `compose.yaml` file mounts the local `./reports` directory into the container at `/reports`. Generated PDFs are therefore visible on the host in `./reports/`. ### βš™οΈ Minimal configuration ```env PVE_API_URL=https://pve.example.invalid:8006 PVE_API_TOKEN_ID=backup-report@pve!report PVE_API_TOKEN_SECRET=change-me REPORT_OUTPUT_DIR=/reports REPORT_TIMEZONE=Europe/Paris PVE_VERIFY_TLS=true ``` To enable PBS collection, provide the URL, token ID and secret for the relevant PBS. An incomplete configuration does not enable PBS collection. Example with one PBS: ```env PBS01_NAME=nom-affiche PBS01_API_URL=https://backup.example.invalid:8007 PBS01_API_TOKEN_ID= PBS01_API_TOKEN_SECRET= ``` One or more PBS servers can be configured. To add a server, duplicate the block and increment the prefix number: `PBS02_*`, `PBS03_*`, `PBS04_*`, etc. The application automatically detects all `PBS_*` blocks present in the environment. ### πŸ—οΈ Build ```sh docker compose build ``` ### βœ… Verification ```sh docker compose run --rm pve-backup-report --check-config docker compose run --rm pve-backup-report --check-api ``` `--check-api` tests the main PVE endpoints. If `/cluster/backup` returns `HTTP 403 - Permission check failed (/, Sys.Audit)`, the token works but is missing the `Sys.Audit` privilege on `/`. ### πŸ“„ Generating the report ```sh docker compose run --rm pve-backup-report --generate-pdf ``` The PDF report is written to `./reports/` with a timestamped filename and never overwrites a previous report. ### πŸ§ͺ Docker diagnostic commands ```sh docker compose run --rm pve-backup-report --dump-inventory docker compose run --rm pve-backup-report --dump-coverage docker compose run --rm pve-backup-report --dump-report-data docker compose run --rm pve-backup-report --dump-pbs-storage-usages docker compose run --rm pve-backup-report --debug-last-backup-vmid ``` Running the container without arguments only executes the default `pve-backup-report` command. To generate a PDF, use `--generate-pdf` explicitly. ### πŸ•’ Scheduling with cron and Docker Example crontab on the host to run the report every day at 02:00: ```cron SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 0 2 * * * cd /srv/pve-backup-report && /usr/bin/flock -n /tmp/pve-backup-report.lock /usr/bin/docker compose run --rm pve-backup-report --generate-pdf >> /var/log/pve-backup-report.log 2>&1 ``` Replace `/srv/pve-backup-report` with the actual path to the repository. The `cd` is important: Docker Compose finds `compose.yaml` there and the application loads `.env` from it. ## πŸ’» Direct command-line usage This mode is useful for development or diagnostics outside a container. The host must have Python, the project's Python dependencies and the system libraries required by WeasyPrint. ### πŸ“¦ Local installation ```sh python3 -m venv .venv . .venv/bin/activate python -m ensurepip --upgrade pip install -r requirements.txt pip install -e . ``` To run the tests: ```sh pip install -e ".[dev]" pytest ``` ### 🧰 Installed commands ```sh pve-backup-report --check-config pve-backup-report --check-api pve-backup-report --dump-inventory pve-backup-report --dump-coverage pve-backup-report --dump-report-data pve-backup-report --dump-pbs-storage-usages pve-backup-report --generate-pdf ``` Without an editable install, from the repository: ```sh PYTHONPATH=src python3 -m pve_backup_report --check-config PYTHONPATH=src python3 -m pve_backup_report --check-api PYTHONPATH=src python3 -m pve_backup_report --dump-inventory PYTHONPATH=src python3 -m pve_backup_report --dump-coverage PYTHONPATH=src python3 -m pve_backup_report --dump-report-data PYTHONPATH=src python3 -m pve_backup_report --dump-pbs-storage-usages PYTHONPATH=src python3 -m pve_backup_report --generate-pdf ``` In direct local execution, `REPORT_OUTPUT_DIR` can remain `reports/` or point to any other directory writable by the current user. The same `.env` file can be used in Docker and in local execution. If `REPORT_OUTPUT_DIR=/reports` is kept outside Docker and `/reports` is not accessible, the application automatically falls back to the local `reports/` directory and logs the fallback. ### πŸ•’ Scheduling with cron without Docker Example using the project's virtual environment: ```cron SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 0 2 * * * cd /srv/pve-backup-report && /usr/bin/flock -n /tmp/pve-backup-report.lock /srv/pve-backup-report/.venv/bin/pve-backup-report --generate-pdf >> /var/log/pve-backup-report.log 2>&1 ``` The user running the crontab must have read access to `.env` and write access to `REPORT_OUTPUT_DIR`. ## πŸ”§ Key variables - 🌐 `PVE_API_URL`: URL of a reachable PVE node, e.g. `https://pve.example.invalid:8006`. - πŸ”‘ `PVE_API_TOKEN_ID`: full token identifier, e.g. `backup-report@pve!report`. - πŸ”’ `PVE_API_TOKEN_SECRET`: token secret. - πŸ›‘οΈ `PVE_VERIFY_TLS`: keep `true` in production; use `PVE_CA_BUNDLE` for an internal CA. - πŸ•’ `PVE_TASK_HISTORY_LIMIT`: number of recent PVE tasks inspected to find the latest backup. - πŸ“œ `PVE_TASK_LOG_LIMIT`: number of lines retrieved per `vzdump` log to extract per-VM/CT detail. - πŸ—ΊοΈ `PBS_HOSTNAMES`: optional manual mapping of PBS servers as `address=display-name,address2=display-name2`. - πŸ—„οΈ `PBS_*`: optional PBS API configurations, e.g. `PBS01_*`, `PBS02_*`, `PBS10_*`. - 🏷️ `REPORT_FILENAME_PREFIX`: prefix for the generated PDF filename. ## πŸ“„ PDF output `--generate-pdf` generates a timestamped PDF in `REPORT_OUTPUT_DIR`. The `Last backup` column shows the status, date, time and duration when PVE provides that information. The `PBS VM/CT backup retention ` tables are split by namespace and show the datastore, the number of PBS versions, and the oldest and most recent backup visible on each configured PBS. πŸ€– AI Usage The application was entirely coded using Codex and Claude Code. I would never have had enough time to build the application on my own in such a short period of time. ## βš–οΈ License This project is distributed under the Apache License 2.0. See the `LICENSE` file.