ProxmoxBackupClientPBSClien.../pbs-client-installer.sh
zaphod-black a828f7eb1f Add live backup progress monitoring with automatic completion detection
User Experience Enhancement: Show real-time backup progress instead
of just asking if user wants to follow logs.

Changes to Backup Progress Display:

1. Automatic Progress Monitoring:
   - No longer prompts to follow logs
   - Automatically starts showing journalctl output
   - Displays PBS client's built-in progress bars and stats
   - Runs in background with PID tracking

2. Visual Progress Box:
   ╔════════════════════════════════════════════════════════╗
   ║  Backup Progress (Live)                                ║
   ║  Press Ctrl+C to exit (backup continues in background) ║
   ╚════════════════════════════════════════════════════════╝

3. Automatic Completion Detection:
   - Monitors systemd service with `systemctl is-active`
   - Polls every 2 seconds until service completes
   - Kills journal follow process when done
   - Shows final status (success or failure)

4. Helpful Post-Backup Information:
   - On success: Shows how to list snapshots
   - On failure: Shows how to check full logs
   - Includes repository info for easy copy/paste

Applied to Two Functions:
- run_backup_now() - Called after initial installation
- Main menu option 4 - "Run backup now"

Technical Implementation:
- Uses `timeout 3600` to prevent runaway journal process
- Background journal follow with PID capture
- Clean process termination with kill + wait
- Proper error handling for failed backups

CHANGELOG.md Updates:
- Added new "Live backup progress monitoring" feature
- Listed all capabilities and use cases

This provides immediate visual feedback and shows the user
exactly what's happening during the backup process, including
file counts, transfer speeds, and compression stats from PBS client.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 18:31:12 -05:00

1069 lines
34 KiB
Bash
Executable file

#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Script configuration
SCRIPT_VERSION="1.0.0"
CONFIG_DIR="/etc/proxmox-backup-client"
LOG_FILE="/var/log/pbs-client-installer.log"
# Helper functions
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
echo "[ERROR] $1" >> "$LOG_FILE"
}
warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
echo "[WARN] $1" >> "$LOG_FILE"
}
info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
prompt() {
local prompt_text="$1"
local default_value="$2"
local user_input
if [ -n "$default_value" ]; then
read -p "$(echo -e ${BLUE}${prompt_text}${NC} [${default_value}]: )" user_input
echo "${user_input:-$default_value}"
else
read -p "$(echo -e ${BLUE}${prompt_text}${NC}: )" user_input
echo "$user_input"
fi
}
prompt_password() {
local prompt_text="$1"
local user_input
read -sp "$(echo -e ${BLUE}${prompt_text}${NC}: )" user_input
echo >&2 # Output newline to stderr so it doesn't get captured
echo "$user_input"
}
# Check if running as root
check_root() {
if [ "$EUID" -ne 0 ]; then
error "This script must be run as root (use sudo)"
exit 1
fi
}
# Detect Linux distribution
detect_distro() {
log "Detecting Linux distribution..."
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
OS_VERSION=$VERSION_ID
OS_PRETTY=$PRETTY_NAME
elif [ -f /etc/lsb-release ]; then
. /etc/lsb-release
OS=$DISTRIB_ID
OS_VERSION=$DISTRIB_RELEASE
OS_PRETTY=$DISTRIB_DESCRIPTION
else
error "Cannot detect Linux distribution"
exit 1
fi
info "Detected: $OS_PRETTY"
# Normalize OS names
OS=$(echo "$OS" | tr '[:upper:]' '[:lower:]')
}
# Install PBS client on Ubuntu
install_ubuntu() {
log "Installing Proxmox Backup Client on Ubuntu $OS_VERSION..."
# Determine which repository to use
if [[ "$OS_VERSION" == "24.04" ]] || [[ "$OS_VERSION" > "24" ]]; then
REPO="bookworm"
GPG_FILE="proxmox-release-bookworm.gpg"
elif [[ "$OS_VERSION" == "22.04" ]]; then
REPO="bullseye"
GPG_FILE="proxmox-release-bullseye.gpg"
# Need to add Focal security repo for libssl1.1
NEED_FOCAL=true
elif [[ "$OS_VERSION" == "20.04" ]]; then
REPO="bullseye"
GPG_FILE="proxmox-release-bullseye.gpg"
else
error "Unsupported Ubuntu version: $OS_VERSION"
exit 1
fi
# Download and install GPG key
log "Downloading GPG key..."
wget -q "https://enterprise.proxmox.com/debian/${GPG_FILE}" \
-O "/etc/apt/trusted.gpg.d/${GPG_FILE}"
# Add PBS client repository
log "Adding PBS client repository..."
echo "deb [arch=amd64] http://download.proxmox.com/debian/pbs-client ${REPO} main" > \
/etc/apt/sources.list.d/pbs-client.list
# Add Focal security repo if needed (Ubuntu 22.04)
if [ "$NEED_FOCAL" = true ]; then
log "Adding Ubuntu Focal security repository for libssl1.1..."
echo "deb [arch=amd64] http://security.ubuntu.com/ubuntu focal-security main" > \
/etc/apt/sources.list.d/focal-security.list
fi
# Update and install
log "Updating package lists..."
apt-get update -qq
log "Installing proxmox-backup-client..."
apt-get install -y proxmox-backup-client
log "PBS client installed successfully"
}
# Install PBS client on Debian
install_debian() {
log "Installing Proxmox Backup Client on Debian $OS_VERSION..."
# Determine repository based on Debian version
case "$OS_VERSION" in
12*)
REPO="bookworm"
GPG_FILE="proxmox-release-bookworm.gpg"
;;
11*)
REPO="bullseye"
GPG_FILE="proxmox-release-bullseye.gpg"
;;
10*)
REPO="buster"
GPG_FILE="proxmox-release-buster.gpg"
;;
*)
error "Unsupported Debian version: $OS_VERSION"
exit 1
;;
esac
# Download and install GPG key
log "Downloading GPG key..."
wget -q "https://enterprise.proxmox.com/debian/${GPG_FILE}" \
-O "/etc/apt/trusted.gpg.d/${GPG_FILE}"
# Add PBS client repository
log "Adding PBS client repository..."
echo "deb [arch=amd64] http://download.proxmox.com/debian/pbs-client ${REPO} main" > \
/etc/apt/sources.list.d/pbs-client.list
# Update and install
log "Updating package lists..."
apt-get update -qq
log "Installing proxmox-backup-client..."
apt-get install -y proxmox-backup-client
log "PBS client installed successfully"
}
# Install PBS client on Arch Linux
install_arch() {
log "Installing Proxmox Backup Client on Arch Linux..."
# Check if yay is installed
if ! command -v yay &> /dev/null; then
error "yay AUR helper is required but not installed"
info "Install yay first: https://github.com/Jguer/yay"
exit 1
fi
# Get the user who invoked sudo
if [ -n "$SUDO_USER" ]; then
REAL_USER="$SUDO_USER"
else
error "Cannot determine original user. Run with sudo."
exit 1
fi
log "Installing proxmox-backup-client-bin from AUR..."
sudo -u "$REAL_USER" yay -S --noconfirm proxmox-backup-client-bin
log "PBS client installed successfully"
}
# Main installation function
install_pbs_client() {
case "$OS" in
ubuntu)
install_ubuntu
;;
debian)
install_debian
;;
arch|archlinux)
install_arch
;;
*)
error "Unsupported distribution: $OS"
info "Supported distributions: Ubuntu, Debian, Arch Linux"
exit 1
;;
esac
# Verify installation
if command -v proxmox-backup-client &> /dev/null; then
local version=$(proxmox-backup-client version 2>/dev/null | head -n1)
log "Installation verified: $version"
return 0
else
error "Installation verification failed"
return 1
fi
}
# Reconfigure connection only (PBS server and credentials)
reconfigure_connection() {
log "Reconfiguring PBS server connection..."
echo
echo "======================================"
echo " PBS Server Connection Setup"
echo "======================================"
echo
# PBS Server details
PBS_SERVER=$(prompt "Enter PBS server IP/hostname" "192.168.1.181")
PBS_PORT=$(prompt "Enter PBS server port" "8007")
PBS_DATASTORE=$(prompt "Enter datastore name" "backups")
echo
info "Authentication Method:"
echo " 1) Username + Password"
echo " 2) API Token (recommended for automation)"
AUTH_METHOD=$(prompt "Select authentication method [1/2]" "2")
if [ "$AUTH_METHOD" = "1" ]; then
PBS_USERNAME=$(prompt "Enter username" "root")
PBS_REALM=$(prompt "Enter realm" "pam")
PBS_PASSWORD=$(prompt_password "Enter password")
PBS_REPOSITORY="${PBS_USERNAME}@${PBS_REALM}@${PBS_SERVER}:${PBS_PORT}:${PBS_DATASTORE}"
else
PBS_USERNAME=$(prompt "Enter username" "backup")
PBS_REALM=$(prompt "Enter realm" "pam")
PBS_TOKEN_NAME=$(prompt "Enter token name" "backup-token")
PBS_TOKEN_SECRET=$(prompt_password "Enter token secret")
PBS_REPOSITORY="${PBS_USERNAME}@${PBS_REALM}!${PBS_TOKEN_NAME}@${PBS_SERVER}:${PBS_PORT}:${PBS_DATASTORE}"
PBS_PASSWORD="$PBS_TOKEN_SECRET"
fi
# Test the new connection
if ! test_connection; then
error "Connection test failed with new credentials"
RETRY=$(prompt "Try again? (yes/no)" "yes")
if [[ "$RETRY" == "yes" ]]; then
reconfigure_connection
return
else
error "Cannot proceed without successful connection"
exit 1
fi
fi
# Load existing configuration and update only connection details
if [ -f "$CONFIG_DIR/config" ]; then
log "Loading existing backup configuration..."
source "$CONFIG_DIR/config"
else
error "No existing configuration found. Please run full configuration."
exit 1
fi
# Update config file with new connection details
log "Updating configuration file..."
# Strip any trailing newlines from password (defensive fix)
PBS_PASSWORD_CLEAN=$(echo -n "$PBS_PASSWORD" | tr -d '\n\r')
cat > "$CONFIG_DIR/config" <<EOF
# PBS Client Configuration
PBS_REPOSITORY="${PBS_REPOSITORY}"
PBS_PASSWORD="${PBS_PASSWORD_CLEAN}"
BACKUP_TYPE="${BACKUP_TYPE}"
BACKUP_PATHS="${BACKUP_PATHS}"
EXCLUDE_PATTERNS="${EXCLUDE_PATTERNS}"
BLOCK_DEVICE="${BLOCK_DEVICE}"
KEEP_LAST=${KEEP_LAST}
KEEP_DAILY=${KEEP_DAILY}
KEEP_WEEKLY=${KEEP_WEEKLY}
KEEP_MONTHLY=${KEEP_MONTHLY}
EOF
chmod 600 "$CONFIG_DIR/config"
log "Connection configuration updated successfully!"
echo
info "Updated Connection:"
echo " PBS Server: ${PBS_SERVER}:${PBS_PORT}"
echo " Datastore: ${PBS_DATASTORE}"
echo " Repository: ${PBS_REPOSITORY}"
echo
info "All other backup settings remain unchanged."
}
# Interactive configuration
interactive_config() {
log "Starting interactive configuration..."
echo
echo "======================================"
echo " PBS Client Configuration"
echo "======================================"
echo
# PBS Server details
PBS_SERVER=$(prompt "Enter PBS server IP/hostname" "192.168.1.181")
PBS_PORT=$(prompt "Enter PBS server port" "8007")
PBS_DATASTORE=$(prompt "Enter datastore name" "backups")
echo
info "Authentication Method:"
echo " 1) Username + Password"
echo " 2) API Token (recommended for automation)"
AUTH_METHOD=$(prompt "Select authentication method [1/2]" "2")
if [ "$AUTH_METHOD" = "1" ]; then
PBS_USERNAME=$(prompt "Enter username" "root")
PBS_REALM=$(prompt "Enter realm" "pam")
PBS_PASSWORD=$(prompt_password "Enter password")
PBS_REPOSITORY="${PBS_USERNAME}@${PBS_REALM}@${PBS_SERVER}:${PBS_PORT}:${PBS_DATASTORE}"
else
PBS_USERNAME=$(prompt "Enter username" "backup")
PBS_REALM=$(prompt "Enter realm" "pam")
PBS_TOKEN_NAME=$(prompt "Enter token name" "backup-token")
PBS_TOKEN_SECRET=$(prompt_password "Enter token secret")
PBS_REPOSITORY="${PBS_USERNAME}@${PBS_REALM}!${PBS_TOKEN_NAME}@${PBS_SERVER}:${PBS_PORT}:${PBS_DATASTORE}"
PBS_PASSWORD="$PBS_TOKEN_SECRET"
fi
echo
# Backup type selection
info "Backup Type:"
echo " 1) File-level only (.pxar) - Fast, efficient, selective restore"
echo " 2) Block device only (.img) - Full disk image, bootable as VM"
echo " 3) Both (Hybrid) - Daily files + Weekly block device (recommended)"
BACKUP_TYPE_CHOICE=$(prompt "Select backup type [1/2/3]" "3")
case "$BACKUP_TYPE_CHOICE" in
1)
BACKUP_TYPE="files"
;;
2)
BACKUP_TYPE="block"
;;
3)
BACKUP_TYPE="both"
;;
*)
warn "Invalid choice, defaulting to hybrid (both)"
BACKUP_TYPE="both"
;;
esac
echo
# Backup configuration
info "Backup Configuration:"
if [[ "$BACKUP_TYPE" == "files" ]] || [[ "$BACKUP_TYPE" == "both" ]]; then
BACKUP_PATHS=$(prompt "Enter paths to backup (space-separated)" "/")
EXCLUDE_PATTERNS=$(prompt "Enter exclusion patterns (space-separated)" "/tmp /var/tmp /var/cache /proc /sys /dev /run")
fi
if [[ "$BACKUP_TYPE" == "block" ]] || [[ "$BACKUP_TYPE" == "both" ]]; then
echo
info "Block Device Configuration:"
# Try to auto-detect root device
# Strip subvolume notation [/@], partition numbers, and trailing 'p'
ROOT_DEVICE=$(findmnt -n -o SOURCE / | sed 's/\[.*\]$//' | sed 's/[0-9]*$//' | sed 's/p$//' 2>/dev/null || echo "")
if [ -n "$ROOT_DEVICE" ] && [ -b "$ROOT_DEVICE" ]; then
info "Auto-detected root device: $ROOT_DEVICE"
BLOCK_DEVICE=$(prompt "Enter block device to backup" "$ROOT_DEVICE")
else
if [ -n "$ROOT_DEVICE" ]; then
warn "Detected device $ROOT_DEVICE is not a valid block device"
fi
info "Common devices: /dev/sda, /dev/nvme0n1, /dev/vda"
BLOCK_DEVICE=$(prompt "Enter block device to backup" "/dev/sda")
fi
# Verify it's a block device
if [ ! -b "$BLOCK_DEVICE" ]; then
warn "Warning: $BLOCK_DEVICE does not exist or is not a block device"
info "Available block devices:"
lsblk -d -n -o NAME,SIZE,TYPE | grep disk | awk '{print " /dev/"$1" ("$2")"}'
echo
CONTINUE=$(prompt "Continue anyway? (yes/no)" "no")
if [[ "$CONTINUE" != "yes" ]]; then
error "Installation cancelled"
exit 1
fi
fi
fi
echo
info "Backup Schedule:"
echo " 1) Hourly"
echo " 2) Daily (recommended)"
echo " 3) Weekly"
echo " 4) Custom"
SCHEDULE_TYPE=$(prompt "Select schedule type [1/2/3/4]" "2")
case "$SCHEDULE_TYPE" in
1)
TIMER_SCHEDULE="hourly"
TIMER_ONCALENDAR="hourly"
;;
2)
BACKUP_HOUR=$(prompt "Enter hour for daily backup (0-23)" "2")
TIMER_SCHEDULE="daily"
TIMER_ONCALENDAR="*-*-* ${BACKUP_HOUR}:00:00"
;;
3)
BACKUP_DOW=$(prompt "Enter day of week (Mon-Sun)" "Sun")
BACKUP_HOUR=$(prompt "Enter hour (0-23)" "2")
TIMER_SCHEDULE="weekly"
TIMER_ONCALENDAR="${BACKUP_DOW} *-*-* ${BACKUP_HOUR}:00:00"
;;
4)
TIMER_ONCALENDAR=$(prompt "Enter systemd OnCalendar format" "*-*-* 02:00:00")
TIMER_SCHEDULE="custom"
;;
esac
echo
# Retention policy
info "Retention Policy:"
KEEP_LAST=$(prompt "Keep last N backups" "3")
KEEP_DAILY=$(prompt "Keep daily backups for N days" "7")
KEEP_WEEKLY=$(prompt "Keep weekly backups for N weeks" "4")
KEEP_MONTHLY=$(prompt "Keep monthly backups for N months" "6")
# Encryption
echo
ENABLE_ENCRYPTION=$(prompt "Enable encryption? (yes/no)" "no")
}
# Create encryption key
create_encryption_key() {
if [[ "$ENABLE_ENCRYPTION" == "yes" ]]; then
log "Creating encryption key..."
# Create config directory for root
mkdir -p /root/.config/proxmox-backup
# Generate key without password (for automated backups)
if proxmox-backup-client key create --kdf none 2>/dev/null; then
log "Encryption key created successfully"
# Create paper backup
local paper_key="/root/pbs-encryption-key-$(date +%Y%m%d).txt"
proxmox-backup-client key paperkey --output-format text > "$paper_key"
warn "IMPORTANT: Encryption key paper backup saved to: $paper_key"
warn "Print this file and store it securely. Lost keys = permanent data loss!"
# Copy to config dir for service
cp /root/.config/proxmox-backup/encryption-key.json "$CONFIG_DIR/"
else
error "Failed to create encryption key"
return 1
fi
fi
}
# Test connection to PBS
test_connection() {
log "Testing connection to PBS server..."
export PBS_REPOSITORY="$PBS_REPOSITORY"
export PBS_PASSWORD="$PBS_PASSWORD"
# Step 1: Verify server is reachable
info "Step 1/3: Checking if server is reachable..."
if ! timeout 5 bash -c "curl -sk --max-time 5 https://${PBS_SERVER}:${PBS_PORT} >/dev/null 2>&1"; then
echo
error "Server is not reachable at https://${PBS_SERVER}:${PBS_PORT}"
error "Possible issues:"
error " - PBS server is down or unreachable"
error " - Wrong IP/hostname"
error " - Firewall blocking port ${PBS_PORT}"
error " - Network connectivity issues"
echo
info "Troubleshooting:"
echo " 1. Verify server is reachable: ping ${PBS_SERVER}"
echo " 2. Test HTTPS connection: curl -k https://${PBS_SERVER}:${PBS_PORT}"
echo " 3. Check if PBS web interface is accessible in browser"
return 1
fi
log "Server is reachable"
# Step 2: Test authentication and handle SSL fingerprint
info "Step 2/3: Testing authentication..."
# Use the 'login' command which tests authentication and stores a ticket
# Automatically accept SSL fingerprint by piping 'y'
local test_output
if test_output=$(echo "y" | timeout 15 proxmox-backup-client login 2>&1); then
# Check if fingerprint was accepted
if echo "$test_output" | grep -q "fingerprint:"; then
local fingerprint=$(echo "$test_output" | grep "fingerprint:" | head -1 | awk '{print $2}')
info "SSL fingerprint accepted: ${fingerprint}"
fi
log "Authentication successful"
else
local exit_code=$?
echo
if [ $exit_code -eq 124 ]; then
error "Authentication test timed out"
error "This might indicate a network issue or server problem"
else
error "Authentication failed"
# Show the actual error from PBS client
if echo "$test_output" | grep -q "error\|Error"; then
echo
echo "$test_output" | grep -i "error" | head -5
echo
fi
fi
error "Possible issues:"
error " - Invalid credentials (username/password/token)"
error " - Datastore '${PBS_DATASTORE}' does not exist"
error " - User lacks permissions for the datastore"
error " - API token is not properly formatted"
error " - SSL certificate issues"
echo
info "Troubleshooting:"
echo " 1. Verify credentials in PBS web interface"
echo " 2. Check datastore name: ${PBS_DATASTORE}"
echo " 3. Verify user has backup permissions"
echo " 4. For API tokens, format is: username@realm!tokenname"
echo " 5. Try the test-connection.sh script for detailed diagnostics"
echo
info "Debug info:"
echo " Repository: ${PBS_REPOSITORY}"
return 1
fi
# Step 3: Verify datastore access by listing backup groups
info "Step 3/3: Verifying datastore access..."
if timeout 15 proxmox-backup-client list 2>/dev/null; then
log "Datastore access verified"
log "Connection test successful!"
return 0
else
warn "Could not list backup groups (this is normal if no backups exist yet)"
log "Connection test successful!"
return 0
fi
}
# Create systemd service
create_systemd_service() {
log "Creating systemd service and timer..."
# Create config directory
mkdir -p "$CONFIG_DIR"
# Strip any trailing newlines from password (defensive fix)
PBS_PASSWORD_CLEAN=$(echo -n "$PBS_PASSWORD" | tr -d '\n\r')
# Save configuration
cat > "$CONFIG_DIR/config" <<EOF
# PBS Client Configuration
PBS_REPOSITORY="${PBS_REPOSITORY}"
PBS_PASSWORD="${PBS_PASSWORD_CLEAN}"
BACKUP_TYPE="${BACKUP_TYPE}"
BACKUP_PATHS="${BACKUP_PATHS}"
EXCLUDE_PATTERNS="${EXCLUDE_PATTERNS}"
BLOCK_DEVICE="${BLOCK_DEVICE}"
KEEP_LAST=${KEEP_LAST}
KEEP_DAILY=${KEEP_DAILY}
KEEP_WEEKLY=${KEEP_WEEKLY}
KEEP_MONTHLY=${KEEP_MONTHLY}
EOF
chmod 600 "$CONFIG_DIR/config"
# Create backup script
cat > "$CONFIG_DIR/backup.sh" <<'EOFSCRIPT'
#!/bin/bash
set -e
# Load configuration
source /etc/proxmox-backup-client/config
# Export for PBS client
export PBS_REPOSITORY
export PBS_PASSWORD
HOSTNAME=$(hostname)
BACKUP_SUCCESS=true
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}
# Function to create file-level backup
backup_files() {
log "Starting file-level backup (.pxar)..."
BACKUP_CMD="proxmox-backup-client backup"
# Add each path as separate archive
for path in $BACKUP_PATHS; do
# Sanitize path for archive name
archive_name=$(echo "$path" | sed 's/^\///' | sed 's/\//-/g')
[ -z "$archive_name" ] && archive_name="root"
BACKUP_CMD="$BACKUP_CMD ${archive_name}.pxar:${path}"
# Add --include-dev for mount points
if mountpoint -q "$path" 2>/dev/null; then
BACKUP_CMD="$BACKUP_CMD --include-dev ${path}"
fi
done
# Add exclusions
for pattern in $EXCLUDE_PATTERNS; do
BACKUP_CMD="$BACKUP_CMD --exclude=${pattern}"
done
# Add other options
BACKUP_CMD="$BACKUP_CMD --skip-lost-and-found --change-detection-mode=metadata"
# Execute backup
if eval $BACKUP_CMD; then
log "File-level backup completed successfully"
return 0
else
log "File-level backup FAILED" >&2
return 1
fi
}
# Function to create block device backup
backup_block_device() {
log "Starting block device backup (.img)..."
if [ -z "$BLOCK_DEVICE" ]; then
log "ERROR: BLOCK_DEVICE not configured" >&2
return 1
fi
if [ ! -b "$BLOCK_DEVICE" ]; then
log "ERROR: $BLOCK_DEVICE is not a block device" >&2
return 1
fi
# Get device size
DEVICE_SIZE=$(blockdev --getsize64 "$BLOCK_DEVICE" 2>/dev/null || echo "0")
DEVICE_SIZE_GB=$((DEVICE_SIZE / 1024 / 1024 / 1024))
log "Backing up device: $BLOCK_DEVICE (${DEVICE_SIZE_GB}GB)"
log "This may take a while..."
# Sanitize device name for archive
DEVICE_NAME=$(basename "$BLOCK_DEVICE")
# Create block device backup
if proxmox-backup-client backup "${DEVICE_NAME}.img:${BLOCK_DEVICE}"; then
log "Block device backup completed successfully"
return 0
else
log "Block device backup FAILED" >&2
return 1
fi
}
# Main backup execution
log "Starting backup for ${HOSTNAME}"
log "Backup type: ${BACKUP_TYPE}"
case "$BACKUP_TYPE" in
files)
backup_files || BACKUP_SUCCESS=false
;;
block)
backup_block_device || BACKUP_SUCCESS=false
;;
both)
# Do file backup first (faster, more frequent)
backup_files || BACKUP_SUCCESS=false
# Only do block device backup on Sunday (weekly)
if [ "$(date +%u)" -eq 7 ]; then
log "Weekly block device backup day (Sunday)"
backup_block_device || BACKUP_SUCCESS=false
else
log "Skipping block device backup (runs weekly on Sunday)"
fi
;;
*)
log "ERROR: Invalid BACKUP_TYPE: ${BACKUP_TYPE}" >&2
exit 1
;;
esac
# Prune old backups
if [ "$BACKUP_SUCCESS" = true ]; then
log "Pruning old backups..."
proxmox-backup-client prune "host/${HOSTNAME}" \
--keep-last $KEEP_LAST \
--keep-daily $KEEP_DAILY \
--keep-weekly $KEEP_WEEKLY \
--keep-monthly $KEEP_MONTHLY
log "Backup and prune completed successfully"
else
log "Backup FAILED" >&2
exit 1
fi
EOFSCRIPT
chmod 700 "$CONFIG_DIR/backup.sh"
# Create systemd service file
cat > /etc/systemd/system/pbs-backup.service <<EOF
[Unit]
Description=Proxmox Backup Client Backup
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=$CONFIG_DIR/backup.sh
StandardOutput=journal
StandardError=journal
SyslogIdentifier=pbs-backup
[Install]
WantedBy=multi-user.target
EOF
# Create systemd timer file
cat > /etc/systemd/system/pbs-backup.timer <<EOF
[Unit]
Description=Proxmox Backup Client Backup Timer
Requires=pbs-backup.service
[Timer]
OnCalendar=${TIMER_ONCALENDAR}
Persistent=true
[Install]
WantedBy=timers.target
EOF
# Reload systemd and enable timer
systemctl daemon-reload
systemctl enable pbs-backup.timer
systemctl start pbs-backup.timer
log "Systemd service and timer created successfully"
# Show timer status
info "Timer status:"
systemctl status pbs-backup.timer --no-pager || true
}
# Run backup immediately
run_backup_now() {
echo
RUN_NOW=$(prompt "Do you want to run a backup now? (yes/no)" "no")
if [[ "$RUN_NOW" == "yes" ]]; then
log "Starting immediate backup..."
echo
# Start the backup
systemctl start pbs-backup.service
# Wait a moment for service to start
sleep 1
# Show real-time progress
echo "╔════════════════════════════════════════════════════════════╗"
echo "║ Backup Progress (Live) ║"
echo "║ Press Ctrl+C to exit (backup continues in background) ║"
echo "╚════════════════════════════════════════════════════════════╝"
echo
# Follow logs to show progress
timeout 3600 journalctl -fu pbs-backup.service &
JOURNAL_PID=$!
# Wait for the service to complete or fail
while systemctl is-active --quiet pbs-backup.service; do
sleep 2
done
# Kill the journal follow
kill $JOURNAL_PID 2>/dev/null || true
wait $JOURNAL_PID 2>/dev/null || true
echo
echo "════════════════════════════════════════════════════════════"
# Check final status
if systemctl status pbs-backup.service | grep -q "Active: failed"; then
echo
error "Backup failed!"
else
echo
log "Backup completed successfully!"
fi
fi
}
# Show summary and next steps
show_summary() {
echo
echo "======================================"
echo " Installation Complete!"
echo "======================================"
echo
info "Configuration Summary:"
echo " PBS Server: ${PBS_SERVER}:${PBS_PORT}"
echo " Datastore: ${PBS_DATASTORE}"
echo " Repository: ${PBS_REPOSITORY}"
echo " Backup Type: ${BACKUP_TYPE}"
if [[ "$BACKUP_TYPE" == "files" ]] || [[ "$BACKUP_TYPE" == "both" ]]; then
echo " Backup Paths: ${BACKUP_PATHS}"
fi
if [[ "$BACKUP_TYPE" == "block" ]] || [[ "$BACKUP_TYPE" == "both" ]]; then
echo " Block Device: ${BLOCK_DEVICE}"
fi
if [[ "$BACKUP_TYPE" == "both" ]]; then
echo " Schedule: Files ${TIMER_SCHEDULE} (${TIMER_ONCALENDAR}), Block device weekly (Sunday)"
else
echo " Schedule: ${TIMER_SCHEDULE} (${TIMER_ONCALENDAR})"
fi
echo
info "Useful Commands:"
echo " Check timer status: sudo systemctl status pbs-backup.timer"
echo " Check service logs: sudo journalctl -u pbs-backup.service"
echo " Run backup now: sudo systemctl start pbs-backup.service"
echo " List backups: sudo -E proxmox-backup-client snapshot list"
echo " Disable backups: sudo systemctl disable pbs-backup.timer"
echo
info "Configuration Files:"
echo " Config: $CONFIG_DIR/config"
echo " Backup script: $CONFIG_DIR/backup.sh"
echo " Service: /etc/systemd/system/pbs-backup.service"
echo " Timer: /etc/systemd/system/pbs-backup.timer"
if [[ "$ENABLE_ENCRYPTION" == "yes" ]]; then
echo
warn "IMPORTANT: Backup your encryption key!"
echo " Key location: /root/.config/proxmox-backup/encryption-key.json"
echo " Paper backup: /root/pbs-encryption-key-*.txt"
fi
echo
log "Setup completed successfully!"
}
# Main script execution
main() {
echo
echo "╔════════════════════════════════════════╗"
echo "║ Proxmox Backup Client Installer ║"
echo "║ Version: ${SCRIPT_VERSION}"
echo "╚════════════════════════════════════════╝"
echo
# Create log file
touch "$LOG_FILE"
# Check root
check_root
# Detect distribution
detect_distro
# Check if PBS client is already installed
if command -v proxmox-backup-client &> /dev/null; then
warn "Proxmox Backup Client is already installed"
# Check if configuration exists
if [ -f "$CONFIG_DIR/config" ]; then
info "Existing configuration detected"
echo
echo "What would you like to do?"
echo " 1) Reconfigure connection only (server/credentials)"
echo " 2) Full reconfiguration (all settings)"
echo " 3) Reinstall PBS client and reconfigure"
echo " 4) Run backup now"
echo " 5) Exit"
ACTION=$(prompt "Select option [1/2/3/4/5]" "1")
case "$ACTION" in
1)
info "Reconfiguring connection only..."
reconfigure_connection
# Skip most of the setup, just restart services
systemctl daemon-reload
systemctl restart pbs-backup.timer
log "Configuration updated and services restarted!"
echo
info "Your backup schedule and settings remain unchanged."
exit 0
;;
2)
info "Proceeding with full reconfiguration..."
# Continue to interactive_config below
;;
3)
info "Reinstalling PBS client..."
install_pbs_client
# Continue to interactive_config below
;;
4)
info "Running backup now..."
echo
# Start the backup
systemctl start pbs-backup.service
# Wait a moment for service to start
sleep 1
# Show real-time progress
echo "╔════════════════════════════════════════════════════════════╗"
echo "║ Backup Progress (Live) ║"
echo "║ Press Ctrl+C to exit (backup continues in background) ║"
echo "╚════════════════════════════════════════════════════════════╝"
echo
# Follow logs to show progress
# Using timeout to automatically stop after backup completes
timeout 3600 journalctl -fu pbs-backup.service &
JOURNAL_PID=$!
# Wait for the service to complete or fail
while systemctl is-active --quiet pbs-backup.service; do
sleep 2
done
# Kill the journal follow
kill $JOURNAL_PID 2>/dev/null || true
wait $JOURNAL_PID 2>/dev/null || true
echo
echo "════════════════════════════════════════════════════════════"
# Check final status
if systemctl status pbs-backup.service | grep -q "Active: failed"; then
echo
error "Backup failed!"
echo
info "Check full logs with:"
echo " sudo journalctl -u pbs-backup.service -n 50"
else
echo
log "Backup completed successfully!"
echo
info "View backup snapshots with:"
echo " export PBS_REPOSITORY='$PBS_REPOSITORY'"
echo " export PBS_PASSWORD='***'"
echo " sudo -E proxmox-backup-client snapshot list"
fi
exit 0
;;
5)
info "Exiting without changes"
exit 0
;;
*)
error "Invalid option"
exit 1
;;
esac
else
# PBS client installed but no config
info "No existing configuration found"
echo
echo "What would you like to do?"
echo " 1) Configure PBS client"
echo " 2) Reinstall and configure"
echo " 3) Exit"
ACTION=$(prompt "Select option [1/2/3]" "1")
case "$ACTION" in
1)
info "Proceeding with configuration..."
# Continue to interactive_config below
;;
2)
info "Reinstalling PBS client..."
install_pbs_client
# Continue to interactive_config below
;;
3)
info "Exiting without changes"
exit 0
;;
*)
error "Invalid option"
exit 1
;;
esac
fi
else
# PBS client not installed
install_pbs_client
fi
# Interactive configuration
interactive_config
# Create encryption key
create_encryption_key
# Test connection
if ! test_connection; then
error "Cannot proceed without successful connection to PBS"
exit 1
fi
# Create systemd service
create_systemd_service
# Offer to run backup now
run_backup_now
# Show summary
show_summary
}
# Run main function
main "$@"