# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview TISBackup is a server-side backup orchestration system written in Python. It executes scheduled backups of various data sources (databases, files, VMs) from remote Linux and Windows systems. The project consists of: - A CLI tool ([tisbackup.py](tisbackup.py)) for executing backups, cleanup, and monitoring - A Flask web GUI ([tisbackup_gui.py](tisbackup_gui.py)) for managing backups - A pluggable backup driver architecture in [libtisbackup/](libtisbackup/) - Task queue system using Huey with Redis ([tasks.py](tasks.py), [config.py](config.py)) - Docker-based deployment with cron scheduling ## Development Commands **IMPORTANT: Always use `uv run` to execute Python commands in this project.** ### Dependency Management ```bash # Install dependencies (uses uv) uv sync --locked # Update dependencies uv lock ``` ### Linting ```bash # Run ruff linter (fast, primary linter) uv run ruff check . # Auto-fix linting issues uv run ruff check --fix . # Run pylint (comprehensive static analysis) uv run pylint libtisbackup/ # Run pylint on specific file uv run pylint libtisbackup/ssh.py ``` ### Testing ```bash # Run all tests uv run pytest # Run tests for specific module uv run pytest tests/test_ssh.py # Run with verbose output uv run pytest -v # Run tests matching a pattern uv run pytest -k "ssh" # Run with coverage report uv run pytest --cov=libtisbackup --cov-report=html --cov-report=term-missing # Run tests with coverage and show only missing lines uv run pytest --cov=libtisbackup --cov-report=term-missing # Generate HTML coverage report (opens in browser) uv run pytest --cov=libtisbackup --cov-report=html # Then open htmlcov/index.html ``` **Coverage reports:** - Terminal report: Shows coverage percentage with missing line numbers - HTML report: Detailed interactive report in `htmlcov/` directory See [tests/README.md](tests/README.md) for detailed testing documentation. ### Running the Application **Web GUI (development):** ```bash uv run python tisbackup_gui.py # Runs on port 8080, requires config at /etc/tis/tisbackup_gui.ini ``` **CLI Commands:** ```bash # Run backups uv run python tisbackup.py -c /etc/tis/tisbackup-config.ini backup # Run specific backup section uv run python tisbackup.py -c /etc/tis/tisbackup-config.ini -s section_name backup # Cleanup old backups uv run python tisbackup.py -c /etc/tis/tisbackup-config.ini cleanup # Check backup status (for Nagios) uv run python tisbackup.py -c /etc/tis/tisbackup-config.ini checknagios # List available backup drivers uv run python tisbackup.py listdrivers ``` ### Docker ```bash # Build image docker build . -t tisbackup:latest # Run via docker compose (see README.md for full setup) docker compose up -d ``` ## Architecture ### Core Components **Main Entry Points:** - [tisbackup.py](tisbackup.py) - CLI application with argument parsing and action routing (backup, cleanup, checknagios, etc.) - [tisbackup_gui.py](tisbackup_gui.py) - Flask web application providing UI for backup management and status monitoring - [tasks.py](tasks.py) - Huey task definitions for async operations (export_backup) **Backup Driver System:** All backup logic is implemented via driver classes in [libtisbackup/drivers/](libtisbackup/drivers/): - Base class: `backup_generic` in [base_driver.py](libtisbackup/base_driver.py) (abstract) - Each driver inherits from `backup_generic` and implements specific backup logic - Drivers are registered via the `register_driver()` decorator function - Configuration is read from INI files using the `read_config()` method - All driver implementations are in [libtisbackup/drivers/](libtisbackup/drivers/) subdirectory **Library Modules:** - [base_driver.py](libtisbackup/base_driver.py) - Core `backup_generic` class, driver registry, Nagios states - [database.py](libtisbackup/database.py) - `BackupStat` class for SQLite operations - [ssh.py](libtisbackup/ssh.py) - SSH utilities with modern key support (Ed25519, ECDSA, RSA) - [process.py](libtisbackup/process.py) - Process execution and monitoring utilities - [utils.py](libtisbackup/utils.py) - Date/time formatting, number formatting, validation helpers - [__init__.py](libtisbackup/__init__.py) - Package exports for backward compatibility - [drivers/](libtisbackup/drivers/) - All backup driver implementations **Available Drivers:** - `backup_rsync` / `backup_rsync_ssh` - File-based backups via rsync - `backup_rsync_btrfs` / `backup_rsync__btrfs_ssh` - Btrfs snapshot-based backups - `backup_mysql` - MySQL database dumps - `backup_pgsql` - PostgreSQL database dumps - `backup_oracle` - Oracle database backups - `backup_sqlserver` - SQL Server backups - `backup_samba4` - Samba4 AD backups - `backup_xva` / `backup_xcp_metadata` / `copy_vm_xcp` - XenServer VM backups - `backup_vmdk` - VMware VMDK backups (requires pyVmomi) - `backup_switch` - Network switch configuration backups - `backup_null` - No-op driver for testing **State Management:** - SQLite database tracks backup history, status, and statistics - `BackupStat` class in [common.py](libtisbackup/common.py) handles DB operations - Database location: `{backup_base_dir}/log/tisbackup.sqlite` ### Configuration Two separate INI configuration files: 1. **tisbackup-config.ini** - Backup definitions - `[global]` section with defaults (backup_base_dir, backup_retention_time, maximum_backup_age) - One section per backup job with driver type and parameters 2. **tisbackup_gui.ini** - GUI settings - Points to tisbackup-config.ini location(s) - Defines admin email, base directories ### Task Queue - Uses Huey (Redis-backed) for async job processing - Currently implements `run_export_backup` for exporting backups to external storage - Task state tracked in tasks.sqlite ### Docker Deployment Two-container architecture: - **tisbackup_gui**: Runs Flask web interface - **tisbackup_cron**: Runs scheduled backups via cron (executes [backup.sh](backup.sh) at 03:59 daily) ## Code Style - Line length: 140 characters (configured in pyproject.toml) - Ruff ignores: F401, F403, F405, E402, E701, E722, E741 - Python 3.13+ required ## Commit Message Guidelines **IMPORTANT: This project uses [Conventional Commits](https://www.conventionalcommits.org/) format.** All commit messages must follow this format: ``` (): [optional body] [optional footer(s)] ``` **Types:** - `feat`: A new feature - `fix`: A bug fix - `docs`: Documentation only changes - `refactor`: Code change that neither fixes a bug nor adds a feature - `test`: Adding missing tests or correcting existing tests - `chore`: Changes to build process or auxiliary tools - `perf`: Performance improvements - `style`: Code style changes (formatting, missing semicolons, etc.) **Scopes (commonly used):** - `auth`: Authentication/authorization changes - `security`: Security-related changes - `drivers`: Backup driver changes - `gui`: Web GUI changes - `api`: API changes - `readme`: README.md changes - `claude`: CLAUDE.md changes - `core`: Core library changes **Examples:** - `feat(auth): add pluggable authentication system for Flask routes` - `fix(security): replace os.popen/os.system with subprocess` - `docs(readme): add comprehensive security and authentication documentation` - `refactor(drivers): organize backup modules into drivers subfolder` - `chore(deps): add pyvmomi as mandatory dependency` **Breaking Changes:** Add `!` after type/scope for breaking changes: - `feat(api)!: remove deprecated endpoint` **Note:** Always include a scope in parentheses, even for documentation changes. When Claude Code creates commits, it will automatically follow this format. ## Important Patterns **Adding a new backup driver:** 1. Create `backup_.py` in [libtisbackup/drivers/](libtisbackup/drivers/) 2. Inherit from `backup_generic` 3. Set class attributes: `type`, `required_params`, `optional_params` 4. Implement abstract methods: `do_backup()`, `cleanup()`, `checknagios()` 5. Register with `register_driver(backup_)` 6. Import in [libtisbackup/drivers/__init__.py](libtisbackup/drivers/__init__.py) **SSH Operations:** - Uses paramiko for SSH connections - Supports both RSA and DSA keys - Private key path specified per backup section via `private_key` parameter - Pre/post-exec hooks run remote commands via SSH **Path Handling:** - Module imports use sys.path manipulation to include lib/ and libtisbackup/ - All backup drivers expect absolute paths for backup_dir - Backup directory structure: `{backup_base_dir}/{section_name}/{timestamp}/`