WIP: Multi-target backup support (Part 1 of 2)

This commit introduces the foundation for multiple backup targets,
allowing users to backup to multiple PBS servers for redundancy.

ADDED:
- Multi-target configuration structure
  - TARGETS_DIR: /etc/proxmox-backup-client/targets/
  - Each target stored as TARGET.conf
  - Independent systemd services per target (pbs-backup-TARGET.service)

- Target management functions:
  - list_targets() - List all configured target names
  - validate_target_name() - Validate target naming rules
  - target_exists() - Check if target is configured
  - get_target_config_path() - Get path to target config file
  - show_targets_list() - Display all targets with server/status
  - show_target_detail() - Show detailed target configuration

- Target CRUD operations:
  - add_target() - Interactive target creation
  - edit_target() - Edit connection, settings, or full reconfig
  - delete_target() - Remove target with confirmation

- Migration system:
  - migrate_legacy_config() - Auto-migrate single-target to "default" target
  - Renames old services: pbs-backup.service -> pbs-backup-default.service
  - Preserves existing backups and schedules

CHANGED:
- Script version: 1.0.0 -> 1.1.0
- Updated CHANGELOG with multi-target progress and recent fixes

WORK IN PROGRESS (Part 2 needed):
- Update create_systemd_service() to accept target name parameter
- Create wrapper functions for per-target configuration
- Update main menu with multi-target options
- Add schedule coordination (all-at-once, alternating, individual)
- Add "Run backup now" with target selection

TESTING STATUS: Not yet tested, foundation only

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zaphod-black 2025-11-01 20:37:31 -05:00
parent e333839c8b
commit d9a4e5a308
2 changed files with 338 additions and 1 deletions

View file

@ -8,6 +8,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- **Multi-target backup support (IN PROGRESS - v1.1.0)**
- Support for multiple backup destinations (different PBS servers for redundancy)
- Named backup targets (e.g., "offsite", "local", "backup1")
- Target management functions:
- List all configured targets with status
- Add new backup targets
- Edit existing targets (connection, settings, or full reconfig)
- Delete targets with confirmation
- View detailed target information
- Automatic migration from legacy single-target configuration to "default" target
- Independent systemd services per target (pbs-backup-TARGET.service)
- Configuration stored in /etc/proxmox-backup-client/targets/TARGET.conf
- Schedule coordination options (planned):
- All targets run at same time
- Alternating schedule across targets
- Individual schedules per target
- Intelligent reconfiguration options when PBS client is already installed
- Quick connection-only reconfiguration (server/credentials only)
- Full reconfiguration of all settings
@ -52,6 +68,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Default realm changed from "pbs" to "pam"** (more common for root authentication)
- **Default encryption setting changed from "yes" to "no"** (user can opt-in if needed)
- Main menu now includes "Run backup now" option for immediate backup testing
- Main menu now includes "Modify backup schedule/type" option (option 5)
- Repository renamed from PBSClientInstaller to PBSClientTool
- **Manual backups now force full backup (files + block device) regardless of day**
- Created separate pbs-backup-manual.service for manual runs
- Scheduled backups still follow daily/weekly pattern
- Manual backups always include block device even on non-Sunday
- Script version bumped to 1.1.0 for multi-target support
### Fixed
- **CRITICAL**: SSL fingerprint prompt no longer causes authentication timeout
@ -71,6 +94,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Shows actual PBS client error messages when authentication fails
- Authentication test now uses correct `login` command instead of non-existent `status` command
- Connection test no longer times out due to using wrong PBS client commands
- **Backup progress logs now display properly during manual backup runs**
- Fixed journalctl following wrong service (was following pbs-backup.service instead of pbs-backup-manual.service)
- Removed stderr suppression (2>/dev/null) that was hiding output
- Added --since flag to capture logs from service start, not just new entries
- Now shows verbose progress: "processed 65.6 GiB in 18m, uploaded 64.4 GiB"
- Backup logs now close automatically when backup completes (no more blank screen)
## [1.0.0] - 2025-11-01

View file

@ -9,8 +9,9 @@ BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Script configuration
SCRIPT_VERSION="1.0.0"
SCRIPT_VERSION="1.1.0"
CONFIG_DIR="/etc/proxmox-backup-client"
TARGETS_DIR="$CONFIG_DIR/targets"
LOG_FILE="/var/log/pbs-client-installer.log"
# Helper functions
@ -55,6 +56,313 @@ prompt_password() {
echo "$user_input"
}
# Multi-target helper functions
list_targets() {
if [ ! -d "$TARGETS_DIR" ]; then
return 1
fi
local targets=()
for config in "$TARGETS_DIR"/*.conf; do
if [ -f "$config" ]; then
targets+=("$(basename "$config" .conf)")
fi
done
if [ ${#targets[@]} -eq 0 ]; then
return 1
fi
printf '%s\n' "${targets[@]}"
}
validate_target_name() {
local name="$1"
# Check if name is empty
if [ -z "$name" ]; then
error "Target name cannot be empty"
return 1
fi
# Check if name contains only alphanumeric, dash, underscore
if ! [[ "$name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
error "Target name can only contain letters, numbers, dash, and underscore"
return 1
fi
return 0
}
target_exists() {
local name="$1"
[ -f "$TARGETS_DIR/$name.conf" ]
}
get_target_config_path() {
local name="$1"
echo "$TARGETS_DIR/$name.conf"
}
migrate_legacy_config() {
# Check if old single-target config exists
if [ -f "$CONFIG_DIR/config" ] && [ ! -d "$TARGETS_DIR" ]; then
log "Migrating legacy configuration to multi-target system..."
mkdir -p "$TARGETS_DIR"
mv "$CONFIG_DIR/config" "$TARGETS_DIR/default.conf"
# Rename old backup script if it exists
if [ -f "$CONFIG_DIR/backup.sh" ]; then
mv "$CONFIG_DIR/backup.sh" "$CONFIG_DIR/backup-default.sh"
fi
# Rename old services if they exist
if [ -f "/etc/systemd/system/pbs-backup.service" ]; then
systemctl stop pbs-backup.service pbs-backup.timer 2>/dev/null || true
systemctl disable pbs-backup.service pbs-backup.timer 2>/dev/null || true
mv /etc/systemd/system/pbs-backup.service /etc/systemd/system/pbs-backup-default.service
mv /etc/systemd/system/pbs-backup-manual.service /etc/systemd/system/pbs-backup-default-manual.service 2>/dev/null || true
mv /etc/systemd/system/pbs-backup.timer /etc/systemd/system/pbs-backup-default.timer
# Update service files to point to new backup script
sed -i 's|/etc/proxmox-backup-client/backup.sh|/etc/proxmox-backup-client/backup-default.sh|g' \
/etc/systemd/system/pbs-backup-default.service \
/etc/systemd/system/pbs-backup-default-manual.service 2>/dev/null || true
systemctl daemon-reload
systemctl enable pbs-backup-default.timer
systemctl start pbs-backup-default.timer
log "Legacy configuration migrated to 'default' target"
fi
fi
}
show_targets_list() {
echo
echo "════════════════════════════════════════"
echo " Configured Backup Targets"
echo "════════════════════════════════════════"
echo
if ! list_targets >/dev/null 2>&1; then
warn "No backup targets configured"
return 1
fi
list_targets | while read -r target; do
local config_file="$(get_target_config_path "$target")"
if [ -f "$config_file" ]; then
source "$config_file"
echo "Target: $target"
echo " Server: ${PBS_SERVER:-unknown}:${PBS_PORT:-8007}"
echo " Datastore: ${PBS_DATASTORE:-unknown}"
echo " Type: ${BACKUP_TYPE:-unknown}"
echo " Schedule: ${TIMER_SCHEDULE:-unknown}"
# Check if timer is active
if systemctl is-enabled "pbs-backup-${target}.timer" &>/dev/null; then
if systemctl is-active "pbs-backup-${target}.timer" &>/dev/null; then
echo " Status: ✓ Active"
else
echo " Status: ✗ Inactive"
fi
else
echo " Status: ✗ Disabled"
fi
echo
fi
done
}
add_target() {
echo
log "Adding new backup target..."
echo
# Get target name
while true; do
TARGET_NAME=$(prompt "Enter target name (e.g., 'offsite', 'local', 'backup1')" "")
if ! validate_target_name "$TARGET_NAME"; then
continue
fi
if target_exists "$TARGET_NAME"; then
error "Target '$TARGET_NAME' already exists"
continue
fi
break
done
# Run interactive config for this target
interactive_config_for_target "$TARGET_NAME"
}
edit_target() {
echo
if ! list_targets >/dev/null 2>&1; then
error "No targets configured"
return 1
fi
echo "Available targets:"
list_targets | nl
echo
TARGET_NAME=$(prompt "Enter target name to edit" "")
if ! validate_target_name "$TARGET_NAME"; then
return 1
fi
if ! target_exists "$TARGET_NAME"; then
error "Target '$TARGET_NAME' does not exist"
return 1
fi
log "Editing target: $TARGET_NAME"
echo
echo "What would you like to edit?"
echo " 1) Connection only (server/credentials)"
echo " 2) Backup settings (type/schedule/retention)"
echo " 3) Full reconfiguration"
echo " 4) Cancel"
local EDIT_CHOICE=$(prompt "Select option [1/2/3/4]" "1")
case "$EDIT_CHOICE" in
1)
reconfigure_connection_for_target "$TARGET_NAME"
;;
2)
reconfigure_backup_settings_for_target "$TARGET_NAME"
;;
3)
interactive_config_for_target "$TARGET_NAME"
;;
4)
info "Cancelled"
return 0
;;
*)
error "Invalid option"
return 1
;;
esac
}
delete_target() {
echo
if ! list_targets >/dev/null 2>&1; then
error "No targets configured"
return 1
fi
echo "Available targets:"
list_targets | nl
echo
TARGET_NAME=$(prompt "Enter target name to delete" "")
if ! validate_target_name "$TARGET_NAME"; then
return 1
fi
if ! target_exists "$TARGET_NAME"; then
error "Target '$TARGET_NAME' does not exist"
return 1
fi
warn "This will delete target '$TARGET_NAME' and stop all associated backups"
local CONFIRM=$(prompt "Are you sure? Type 'yes' to confirm" "no")
if [ "$CONFIRM" != "yes" ]; then
info "Cancelled"
return 0
fi
log "Deleting target: $TARGET_NAME"
# Stop and disable services
systemctl stop "pbs-backup-${TARGET_NAME}.service" "pbs-backup-${TARGET_NAME}.timer" 2>/dev/null || true
systemctl disable "pbs-backup-${TARGET_NAME}.service" "pbs-backup-${TARGET_NAME}.timer" 2>/dev/null || true
# Remove files
rm -f "/etc/systemd/system/pbs-backup-${TARGET_NAME}.service"
rm -f "/etc/systemd/system/pbs-backup-${TARGET_NAME}-manual.service"
rm -f "/etc/systemd/system/pbs-backup-${TARGET_NAME}.timer"
rm -f "$CONFIG_DIR/backup-${TARGET_NAME}.sh"
rm -f "$(get_target_config_path "$TARGET_NAME")"
systemctl daemon-reload
log "Target '$TARGET_NAME' deleted successfully"
}
show_target_detail() {
local target="$1"
if ! target_exists "$target"; then
error "Target '$target' does not exist"
return 1
fi
local config_file="$(get_target_config_path "$target")"
source "$config_file"
echo
echo "════════════════════════════════════════"
echo " Target: $target"
echo "════════════════════════════════════════"
echo
echo "Connection:"
echo " Server: ${PBS_SERVER}:${PBS_PORT}"
echo " Datastore: ${PBS_DATASTORE}"
echo " Repository: ${PBS_REPOSITORY}"
echo
echo "Backup Configuration:"
echo " Type: ${BACKUP_TYPE}"
if [[ "$BACKUP_TYPE" == "files" ]] || [[ "$BACKUP_TYPE" == "both" ]]; then
echo " Paths: ${BACKUP_PATHS}"
echo " Exclusions: ${EXCLUDE_PATTERNS}"
fi
if [[ "$BACKUP_TYPE" == "block" ]] || [[ "$BACKUP_TYPE" == "both" ]]; then
echo " Block Device: ${BLOCK_DEVICE}"
fi
echo
echo "Schedule:"
if [[ "$BACKUP_TYPE" == "both" ]]; then
echo " Files: ${TIMER_SCHEDULE} (${TIMER_ONCALENDAR})"
echo " Block: Weekly on Sunday"
else
echo " ${TIMER_SCHEDULE} (${TIMER_ONCALENDAR})"
fi
echo
echo "Retention:"
echo " Last: ${KEEP_LAST}"
echo " Daily: ${KEEP_DAILY}"
echo " Weekly: ${KEEP_WEEKLY}"
echo " Monthly: ${KEEP_MONTHLY}"
echo
# Show timer status
if systemctl is-enabled "pbs-backup-${target}.timer" &>/dev/null; then
echo "Next scheduled backup:"
systemctl list-timers "pbs-backup-${target}.timer" --no-pager 2>/dev/null || true
else
warn "Timer not enabled for this target"
fi
}
# Check if running as root
check_root() {
if [ "$EUID" -ne 0 ]; then