Files
HomeAudit/scripts/comprehensive_pre_migration_backup_automated.sh
admin 705a2757c1 Major infrastructure migration and Vaultwarden PostgreSQL troubleshooting
COMPREHENSIVE CHANGES:

INFRASTRUCTURE MIGRATION:
- Migrated services to Docker Swarm on OMV800 (192.168.50.229)
- Deployed PostgreSQL database for Vaultwarden migration
- Updated all stack configurations for Docker Swarm compatibility
- Added comprehensive monitoring stack (Prometheus, Grafana, Blackbox)
- Implemented proper secret management for all services

VAULTWARDEN POSTGRESQL MIGRATION:
- Attempted migration from SQLite to PostgreSQL for NFS compatibility
- Created PostgreSQL stack with proper user/password configuration
- Built custom Vaultwarden image with PostgreSQL support
- Troubleshot persistent SQLite fallback issue despite PostgreSQL config
- Identified known issue where Vaultwarden silently falls back to SQLite
- Added ENABLE_DB_WAL=false to prevent filesystem compatibility issues
- Current status: Old Vaultwarden on lenovo410 still working, new one has config issues

PAPERLESS SERVICES:
- Successfully deployed Paperless-NGX and Paperless-AI on OMV800
- Both services running on ports 8000 and 3000 respectively
- Caddy configuration updated for external access
- Services accessible via paperless.pressmess.duckdns.org and paperless-ai.pressmess.duckdns.org

CADDY CONFIGURATION:
- Updated Caddyfile on Surface (192.168.50.254) for new service locations
- Fixed Vaultwarden reverse proxy to point to new Docker Swarm service
- Removed old notification hub reference that was causing conflicts
- All services properly configured for external access via DuckDNS

BACKUP AND DISCOVERY:
- Created comprehensive backup system for all hosts
- Generated detailed discovery reports for infrastructure analysis
- Implemented automated backup validation scripts
- Created migration progress tracking and verification reports

MONITORING STACK:
- Deployed Prometheus, Grafana, and Blackbox monitoring
- Created infrastructure and system overview dashboards
- Added proper service discovery and alerting configuration
- Implemented performance monitoring for all critical services

DOCUMENTATION:
- Reorganized documentation into logical structure
- Created comprehensive migration playbook and troubleshooting guides
- Added hardware specifications and optimization recommendations
- Documented all configuration changes and service dependencies

CURRENT STATUS:
- Paperless services:  Working and accessible externally
- Vaultwarden:  PostgreSQL configuration issues, old instance still working
- Monitoring:  Deployed and operational
- Caddy:  Updated and working for external access
- PostgreSQL:  Database running, connection issues with Vaultwarden

NEXT STEPS:
- Continue troubleshooting Vaultwarden PostgreSQL configuration
- Consider alternative approaches for Vaultwarden migration
- Validate all external service access
- Complete final migration validation

TECHNICAL NOTES:
- Used Docker Swarm for orchestration on OMV800
- Implemented proper secret management for sensitive data
- Added comprehensive logging and monitoring
- Created automated backup and validation scripts
2025-08-30 20:18:44 -04:00

732 lines
30 KiB
Bash
Executable File

#!/bin/bash
# Comprehensive Pre-Migration Backup Script (Automated with Resume Support)
# Uses discovery results and password file to backup 100% of infrastructure
# Based on comprehensive discovery of all services, databases, and data
# Supports resuming from where it left off and shows progress
set -uo pipefail
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
BACKUP_TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/export/omv800_backup/pre_migration_${BACKUP_TIMESTAMP}"
LOG_FILE="$PROJECT_ROOT/logs/comprehensive_backup_${BACKUP_TIMESTAMP}.log"
PASSWORD_FILE="$PROJECT_ROOT/secrets/ssh_passwords.env"
DISCOVERY_DIR="$PROJECT_ROOT/comprehensive_discovery_results"
PROGRESS_FILE="$PROJECT_ROOT/logs/backup_progress_${BACKUP_TIMESTAMP}.json"
# Resume support - check if we're resuming an existing backup
RESUME_BACKUP_DIR=""
if [[ -n "${1:-}" ]]; then
# Check if the directory exists on raspberrypi
if ssh jon@raspberrypi "test -d '$1'" 2>/dev/null; then
RESUME_BACKUP_DIR="$1"
BACKUP_DIR="$RESUME_BACKUP_DIR"
BACKUP_TIMESTAMP=$(basename "$BACKUP_DIR" | sed 's/pre_migration_//')
LOG_FILE="$PROJECT_ROOT/logs/comprehensive_backup_${BACKUP_TIMESTAMP}.log"
PROGRESS_FILE="$PROJECT_ROOT/logs/backup_progress_${BACKUP_TIMESTAMP}.json"
echo "🔄 RESUMING backup from: $BACKUP_DIR"
fi
fi
# Create directories
mkdir -p "$(dirname "$LOG_FILE")"
mkdir -p "$(dirname "$PROGRESS_FILE")"
# Load passwords
if [[ -f "$PASSWORD_FILE" ]]; then
source "$PASSWORD_FILE"
else
echo "Error: Password file not found: $PASSWORD_FILE"
exit 1
fi
# Progress tracking
declare -A COMPLETED_TASKS
declare -A TOTAL_TASKS
# Initialize progress tracking
init_progress() {
TOTAL_TASKS["databases"]=0
TOTAL_TASKS["volumes"]=0
TOTAL_TASKS["configs"]=0
TOTAL_TASKS["secrets"]=0
TOTAL_TASKS["user_data"]=0
TOTAL_TASKS["system_configs"]=0
COMPLETED_TASKS["databases"]=0
COMPLETED_TASKS["volumes"]=0
COMPLETED_TASKS["configs"]=0
COMPLETED_TASKS["secrets"]=0
COMPLETED_TASKS["user_data"]=0
COMPLETED_TASKS["system_configs"]=0
# Count total tasks from discovery results
if [[ -d "$DISCOVERY_DIR" ]]; then
# Count databases
if [[ -f "$DISCOVERY_DIR/databases_fedora.txt" ]]; then
TOTAL_TASKS["databases"]=$(wc -l < "$DISCOVERY_DIR/databases_fedora.txt")
fi
# Count volumes (skip header line)
if [[ -f "$DISCOVERY_DIR/volumes_fedora.txt" ]]; then
TOTAL_TASKS["volumes"]=$(($(wc -l < "$DISCOVERY_DIR/volumes_fedora.txt") - 1))
fi
# Estimate other tasks
TOTAL_TASKS["configs"]=5 # Local + fedora + other hosts
TOTAL_TASKS["secrets"]=5
TOTAL_TASKS["user_data"]=5
TOTAL_TASKS["system_configs"]=5
fi
save_progress
}
# Save progress to file
save_progress() {
cat > "$PROGRESS_FILE" << EOF
{
"timestamp": "$(date -Iseconds)",
"backup_dir": "$BACKUP_DIR",
"completed_tasks": $(printf '%s\n' "${COMPLETED_TASKS[@]}" | jq -R . | jq -s .),
"total_tasks": $(printf '%s\n' "${TOTAL_TASKS[@]}" | jq -R . | jq -s .),
"task_names": ["databases", "volumes", "configs", "secrets", "user_data", "system_configs"]
}
EOF
}
# Load existing progress if resuming
load_progress() {
if [[ -n "$RESUME_BACKUP_DIR" ]]; then
echo "📋 Loading existing progress from backup directory: $BACKUP_DIR"
# Initialize arrays if not already done
COMPLETED_TASKS["databases"]="${COMPLETED_TASKS["databases"]:-0}"
COMPLETED_TASKS["volumes"]="${COMPLETED_TASKS["volumes"]:-0}"
COMPLETED_TASKS["configs"]="${COMPLETED_TASKS["configs"]:-0}"
COMPLETED_TASKS["secrets"]="${COMPLETED_TASKS["secrets"]:-0}"
COMPLETED_TASKS["user_data"]="${COMPLETED_TASKS["user_data"]:-0}"
COMPLETED_TASKS["system_configs"]="${COMPLETED_TASKS["system_configs"]:-0}"
# Parse completed tasks from existing backup files on raspberrypi
if ssh jon@raspberrypi "test -d '$BACKUP_DIR/database_dumps'" 2>/dev/null; then
COMPLETED_TASKS["databases"]=$(ssh jon@raspberrypi "find '$BACKUP_DIR/database_dumps' -name '*.sql' | wc -l" 2>/dev/null || echo "0")
fi
if ssh jon@raspberrypi "test -d '$BACKUP_DIR/docker_volumes'" 2>/dev/null; then
COMPLETED_TASKS["volumes"]=$(ssh jon@raspberrypi "find '$BACKUP_DIR/docker_volumes' -name '*.tar.gz' | wc -l" 2>/dev/null || echo "0")
fi
if ssh jon@raspberrypi "test -d '$BACKUP_DIR/configurations'" 2>/dev/null; then
COMPLETED_TASKS["configs"]=$(ssh jon@raspberrypi "find '$BACKUP_DIR/configurations' -name '*.tar.gz' | wc -l" 2>/dev/null || echo "0")
fi
if ssh jon@raspberrypi "test -d '$BACKUP_DIR/secrets'" 2>/dev/null; then
COMPLETED_TASKS["secrets"]=$(ssh jon@raspberrypi "find '$BACKUP_DIR/secrets' -name '*.tar.gz' | wc -l" 2>/dev/null || echo "0")
fi
if ssh jon@raspberrypi "test -d '$BACKUP_DIR/user_data'" 2>/dev/null; then
COMPLETED_TASKS["user_data"]=$(ssh jon@raspberrypi "find '$BACKUP_DIR/user_data' -name '*.tar.gz' | wc -l" 2>/dev/null || echo "0")
fi
if ssh jon@raspberrypi "test -d '$BACKUP_DIR/system_configs'" 2>/dev/null; then
COMPLETED_TASKS["system_configs"]=$(ssh jon@raspberrypi "find '$BACKUP_DIR/system_configs' -name '*.tar.gz' | wc -l" 2>/dev/null || echo "0")
fi
echo "📊 Loaded progress:"
echo " Databases: ${COMPLETED_TASKS["databases"]}"
echo " Volumes: ${COMPLETED_TASKS["volumes"]}"
echo " Configs: ${COMPLETED_TASKS["configs"]}"
echo " Secrets: ${COMPLETED_TASKS["secrets"]}"
echo " User Data: ${COMPLETED_TASKS["user_data"]}"
echo " System Configs: ${COMPLETED_TASKS["system_configs"]}"
fi
}
# Show progress
show_progress() {
local task_type="$1"
local current="${COMPLETED_TASKS[$task_type]:-0}"
local total="${TOTAL_TASKS[$task_type]:-1}"
local percentage=0
if [[ $total -gt 0 ]]; then
percentage=$((current * 100 / total))
fi
echo "📊 Progress [$task_type]: $current/$total ($percentage%)"
save_progress
}
# Logging function with progress
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
# Error handling
cleanup() {
log "Cleaning up temporary files..."
rm -f /tmp/backup_*.sql /tmp/docker_*.txt /tmp/network_*.txt 2>/dev/null || true
}
trap cleanup EXIT
# SSH function with password support
ssh_with_password() {
local host="$1"
local user="$2"
local command="$3"
local password=""
# Get password for specific host
case "$host" in
"fedora")
password="$FEDORA_PASSWORD"
;;
"lenovo")
password="$LENOVO_PASSWORD"
;;
"lenovo420")
password="$LENOVO420_PASSWORD"
;;
"omv800")
password="$OMV800_PASSWORD"
;;
"surface")
password="$SURFACE_PASSWORD"
;;
"audrey")
password="$AUDREY_PASSWORD"
;;
"raspberrypi")
password="$RASPBERRYPI_PASSWORD"
;;
*)
password=""
;;
esac
if [[ -n "$password" ]]; then
# Use sshpass for password authentication
if command -v sshpass >/dev/null 2>&1; then
sshpass -p "$password" ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$user@$host" "$command"
else
log "Warning: sshpass not available, trying SSH key authentication for $host"
ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$user@$host" "$command"
fi
else
# Try SSH key authentication
ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$user@$host" "$command"
fi
}
# Check if backup already exists (for resume)
backup_exists() {
local backup_path="$1"
ssh jon@raspberrypi "test -f '$backup_path'" 2>/dev/null
}
# Backup databases with progress
backup_all_databases() {
log "=== BACKING UP ALL DATABASES ==="
if [[ -f "$DISCOVERY_DIR/databases_fedora.txt" ]]; then
log "Backing up databases on fedora (user: jonathan)..."
while IFS= read -r line; do
if [[ -z "$line" || "$line" == *"CONTAINER"* ]]; then
continue
fi
local container_name=$(echo "$line" | awk '{print $1}')
local db_type=$(echo "$line" | awk '{print $2}')
local backup_name="fedora_${container_name}_${db_type}_${BACKUP_TIMESTAMP}.sql"
local backup_path="$BACKUP_DIR/database_dumps/$backup_name"
# Check if already backed up
if backup_exists "$backup_path"; then
log "⏭️ Skipping existing database backup: $backup_name"
((COMPLETED_TASKS["databases"]++))
show_progress "databases"
continue
fi
log "🔄 Backing up database: $container_name ($db_type)"
case "$db_type" in
"postgres")
ssh_with_password "fedora" "jonathan" "docker exec $container_name pg_dumpall -U postgres" > "/tmp/${backup_name}" 2>/dev/null || true
;;
"mysql"|"mariadb")
ssh_with_password "fedora" "jonathan" "docker exec $container_name mysqldump -u root -p --all-databases" > "/tmp/${backup_name}" 2>/dev/null || true
;;
"redis")
ssh_with_password "fedora" "jonathan" "docker exec $container_name redis-cli BGSAVE" > "/tmp/${backup_name}" 2>/dev/null || true
;;
"mongodb")
ssh_with_password "fedora" "jonathan" "docker exec $container_name mongodump --out /tmp/mongo_dump" > "/tmp/${backup_name}" 2>/dev/null || true
;;
esac
if [[ -s "/tmp/${backup_name}" ]]; then
rsync -avz "/tmp/${backup_name}" "jon@raspberrypi:$backup_path"
log "✅ Database backup created: $backup_path ($(stat -c%s "/tmp/${backup_name}") bytes)"
((COMPLETED_TASKS["databases"]++))
show_progress "databases"
else
log "⚠️ Empty database backup for: $container_name"
fi
done < "$DISCOVERY_DIR/databases_fedora.txt"
fi
}
# Backup Docker volumes with progress
backup_all_docker_volumes() {
log "=== BACKING UP ALL DOCKER VOLUMES ==="
if [[ -f "$DISCOVERY_DIR/volumes_fedora.txt" ]]; then
log "Backing up Docker volumes on fedora (user: jonathan)..."
# Skip header line and process volumes
local volume_count=0
while IFS= read -r line; do
if [[ -z "$line" ]]; then
continue
fi
local volume_name=$(echo "$line" | awk '{print $2}')
local backup_name="fedora_${volume_name}_${BACKUP_TIMESTAMP}.tar.gz"
local backup_path="$BACKUP_DIR/docker_volumes/$backup_name"
# Check if already backed up
if backup_exists "$backup_path"; then
log "⏭️ Skipping existing volume backup: $backup_name"
((volume_count++))
((COMPLETED_TASKS["volumes"]++))
show_progress "volumes"
continue
fi
log "🔄 Backing up Docker volume: $volume_name on fedora"
# Create volume backup
ssh_with_password "fedora" "jonathan" "docker run --rm -v $volume_name:/data -v /tmp:/backup alpine tar czf /backup/volume_backup.tar.gz -C /data ." 2>/dev/null || true
if ssh_with_password "fedora" "jonathan" "test -f /tmp/volume_backup.tar.gz" 2>/dev/null; then
# Copy from fedora to raspberrypi via local machine
scp "jonathan@fedora:/tmp/volume_backup.tar.gz" "/tmp/volume_backup_temp.tar.gz" 2>/dev/null || true
if [[ -f "/tmp/volume_backup_temp.tar.gz" ]]; then
rsync -avz "/tmp/volume_backup_temp.tar.gz" "jon@raspberrypi:$backup_path"
local size=$(stat -c%s "/tmp/volume_backup_temp.tar.gz" 2>/dev/null || echo "0")
log "✅ Volume backup created: $backup_path ($size bytes)"
rm -f "/tmp/volume_backup_temp.tar.gz" 2>/dev/null || true
ssh_with_password "fedora" "jonathan" "rm -f /tmp/volume_backup.tar.gz" 2>/dev/null || true
((COMPLETED_TASKS["volumes"]++))
show_progress "volumes"
else
log "⚠️ Failed to copy volume backup from fedora"
fi
else
log "⚠️ Failed to create volume backup for: $volume_name"
fi
done < <(tail -n +2 "$DISCOVERY_DIR/volumes_fedora.txt")
fi
}
# Backup configurations with progress
backup_all_configurations() {
log "=== BACKING UP ALL CONFIGURATIONS ==="
# Create configurations directory
ssh jon@raspberrypi "mkdir -p $BACKUP_DIR/configurations"
# Local configs
if ! backup_exists "$BACKUP_DIR/configurations/local_configs_${BACKUP_TIMESTAMP}.tar.gz"; then
log "Backing up local configurations..."
tar czf "/tmp/local_configs_${BACKUP_TIMESTAMP}.tar.gz" -C "$PROJECT_ROOT" . 2>/dev/null || true
if [[ -f "/tmp/local_configs_${BACKUP_TIMESTAMP}.tar.gz" ]]; then
rsync -avz "/tmp/local_configs_${BACKUP_TIMESTAMP}.tar.gz" "jon@raspberrypi:$BACKUP_DIR/configurations/"
local size=$(stat -c%s "/tmp/local_configs_${BACKUP_TIMESTAMP}.tar.gz")
log "✅ Local config backup created: $BACKUP_DIR/configurations/local_configs_${BACKUP_TIMESTAMP}.tar.gz ($size bytes)"
((COMPLETED_TASKS["configs"]++))
show_progress "configs"
fi
else
log "⏭️ Skipping existing local config backup"
((COMPLETED_TASKS["configs"]++))
show_progress "configs"
fi
# Remote configs
if [[ -f "$DISCOVERY_DIR/all_hosts.txt" ]]; then
while IFS=: read -r host user; do
if [[ -z "$host" || "$host" == "localhost" ]]; then
continue
fi
local backup_name="${host}_configs_${BACKUP_TIMESTAMP}.tar.gz"
local backup_path="$BACKUP_DIR/configurations/$backup_name"
# Check if already backed up
if backup_exists "$backup_path"; then
log "⏭️ Skipping existing config backup for $host"
((COMPLETED_TASKS["configs"]++))
show_progress "configs"
continue
fi
log "Backing up configurations on $host (user: $user)..."
log "Backing up configurations for $host"
# Create config backup
if ssh_with_password "$host" "$user" "tar czf /tmp/config_backup.tar.gz -C /etc . -C /home . 2>/dev/null || true" 2>/dev/null; then
if ssh_with_password "$host" "$user" "test -f /tmp/config_backup.tar.gz" 2>/dev/null; then
# Copy from host to raspberrypi via local machine
scp "$user@$host:/tmp/config_backup.tar.gz" "/tmp/config_backup_temp.tar.gz" 2>/dev/null || true
if [[ -f "/tmp/config_backup_temp.tar.gz" ]]; then
rsync -avz "/tmp/config_backup_temp.tar.gz" "jon@raspberrypi:$backup_path"
local size=$(stat -c%s "/tmp/config_backup_temp.tar.gz" 2>/dev/null || echo "0")
log "✅ Config backup created: $backup_path ($size bytes)"
rm -f "/tmp/config_backup_temp.tar.gz" 2>/dev/null || true
ssh_with_password "$host" "$user" "rm -f /tmp/config_backup.tar.gz" 2>/dev/null || true
((COMPLETED_TASKS["configs"]++))
show_progress "configs"
else
log "⚠️ Failed to copy config backup from $host"
fi
else
log "⚠️ Failed to create config backup for $host"
fi
else
log "⚠️ Failed to connect to $host for config backup"
fi
done < "$DISCOVERY_DIR/all_hosts.txt"
fi
}
# Backup secrets with progress
backup_all_secrets() {
log "=== BACKING UP ALL SECRETS AND SSL CERTIFICATES ==="
# Create secrets directory
ssh jon@raspberrypi "mkdir -p $BACKUP_DIR/secrets"
# Local secrets
if ! backup_exists "$BACKUP_DIR/secrets/local_secrets_${BACKUP_TIMESTAMP}.tar.gz"; then
log "Backing up local secrets..."
tar czf "/tmp/local_secrets_${BACKUP_TIMESTAMP}.tar.gz" -C "$PROJECT_ROOT/secrets" . 2>/dev/null || true
if [[ -f "/tmp/local_secrets_${BACKUP_TIMESTAMP}.tar.gz" ]]; then
rsync -avz "/tmp/local_secrets_${BACKUP_TIMESTAMP}.tar.gz" "jon@raspberrypi:$BACKUP_DIR/secrets/"
local size=$(stat -c%s "/tmp/local_secrets_${BACKUP_TIMESTAMP}.tar.gz")
log "✅ Local secrets backup created: $BACKUP_DIR/secrets/local_secrets_${BACKUP_TIMESTAMP}.tar.gz ($size bytes)"
((COMPLETED_TASKS["secrets"]++))
show_progress "secrets"
fi
else
log "⏭️ Skipping existing local secrets backup"
((COMPLETED_TASKS["secrets"]++))
show_progress "secrets"
fi
# Remote secrets
if [[ -f "$DISCOVERY_DIR/all_hosts.txt" ]]; then
while IFS=: read -r host user; do
if [[ -z "$host" || "$host" == "localhost" ]]; then
continue
fi
local backup_name="${host}_secrets_${BACKUP_TIMESTAMP}.tar.gz"
local backup_path="$BACKUP_DIR/secrets/$backup_name"
# Check if already backed up
if backup_exists "$backup_path"; then
log "⏭️ Skipping existing secrets backup for $host"
((COMPLETED_TASKS["secrets"]++))
show_progress "secrets"
continue
fi
log "Backing up SSL certificates on $host (user: $user)..."
log "Backing up secrets for $host"
# Create secrets backup
if ssh_with_password "$host" "$user" "tar czf /tmp/secrets_backup.tar.gz -C /etc/ssl . -C /etc/letsencrypt . 2>/dev/null || true" 2>/dev/null; then
if ssh_with_password "$host" "$user" "test -f /tmp/secrets_backup.tar.gz" 2>/dev/null; then
# Copy from host to raspberrypi via local machine
scp "$user@$host:/tmp/secrets_backup.tar.gz" "/tmp/secrets_backup_temp.tar.gz" 2>/dev/null || true
if [[ -f "/tmp/secrets_backup_temp.tar.gz" ]]; then
rsync -avz "/tmp/secrets_backup_temp.tar.gz" "jon@raspberrypi:$backup_path"
local size=$(stat -c%s "/tmp/secrets_backup_temp.tar.gz" 2>/dev/null || echo "0")
log "✅ Secrets backup created: $backup_path ($size bytes)"
rm -f "/tmp/secrets_backup_temp.tar.gz" 2>/dev/null || true
ssh_with_password "$host" "$user" "rm -f /tmp/secrets_backup.tar.gz" 2>/dev/null || true
((COMPLETED_TASKS["secrets"]++))
show_progress "secrets"
else
log "⚠️ Failed to copy secrets backup from $host"
fi
else
log "⚠️ Failed to create secrets backup for $host"
fi
else
log "⚠️ Failed to connect to $host for secrets backup"
fi
done < "$DISCOVERY_DIR/all_hosts.txt"
fi
}
# Backup user data with progress
backup_all_user_data() {
log "=== BACKING UP ALL USER DATA AND APPLICATIONS ==="
# Create user_data directory
ssh jon@raspberrypi "mkdir -p $BACKUP_DIR/user_data"
if [[ -f "$DISCOVERY_DIR/all_hosts.txt" ]]; then
while IFS=: read -r host user; do
if [[ -z "$host" || "$host" == "localhost" ]]; then
continue
fi
local backup_name="${host}_user_data_${BACKUP_TIMESTAMP}.tar.gz"
local backup_path="$BACKUP_DIR/user_data/$backup_name"
# Check if already backed up
if backup_exists "$backup_path"; then
log "⏭️ Skipping existing user data backup for $host"
((COMPLETED_TASKS["user_data"]++))
show_progress "user_data"
continue
fi
log "Backing up user data on $host (user: $user)..."
log "Backing up user data for $host"
# Create user data backup (excluding large directories that might cause issues)
if ssh_with_password "$host" "$user" "tar czf /tmp/user_data_backup.tar.gz --exclude='*/node_modules' --exclude='*/.git' --exclude='*/Downloads' --exclude='*/Videos' --exclude='*/Music' -C /home . -C /srv . 2>/dev/null || true" 2>/dev/null; then
if ssh_with_password "$host" "$user" "test -f /tmp/user_data_backup.tar.gz" 2>/dev/null; then
# Copy from host to raspberrypi via local machine
scp "$user@$host:/tmp/user_data_backup.tar.gz" "/tmp/user_data_backup_temp.tar.gz" 2>/dev/null || true
if [[ -f "/tmp/user_data_backup_temp.tar.gz" ]]; then
rsync -avz "/tmp/user_data_backup_temp.tar.gz" "jon@raspberrypi:$backup_path"
local size=$(stat -c%s "/tmp/user_data_backup_temp.tar.gz" 2>/dev/null || echo "0")
log "✅ User data backup created: $backup_path ($size bytes)"
rm -f "/tmp/user_data_backup_temp.tar.gz" 2>/dev/null || true
ssh_with_password "$host" "$user" "rm -f /tmp/user_data_backup.tar.gz" 2>/dev/null || true
((COMPLETED_TASKS["user_data"]++))
show_progress "user_data"
else
log "⚠️ Failed to copy user data backup from $host"
fi
else
log "⚠️ Failed to create user data backup for $host"
fi
else
log "⚠️ Failed to connect to $host for user data backup"
fi
done < "$DISCOVERY_DIR/all_hosts.txt"
fi
}
# Backup system configurations with progress
backup_all_system_configs() {
log "=== BACKING UP ALL SYSTEM CONFIGURATIONS ==="
# Create system_configs directory
ssh jon@raspberrypi "mkdir -p $BACKUP_DIR/system_configs"
if [[ -f "$DISCOVERY_DIR/all_hosts.txt" ]]; then
while IFS=: read -r host user; do
if [[ -z "$host" || "$host" == "localhost" ]]; then
continue
fi
local backup_name="${host}_system_configs_${BACKUP_TIMESTAMP}.tar.gz"
local backup_path="$BACKUP_DIR/system_configs/$backup_name"
# Check if already backed up
if backup_exists "$backup_path"; then
log "⏭️ Skipping existing system config backup for $host"
((COMPLETED_TASKS["system_configs"]++))
show_progress "system_configs"
continue
fi
log "Backing up system configurations on $host (user: $user)..."
# Create system config backup
if ssh_with_password "$host" "$user" "tar czf /tmp/system_configs_backup.tar.gz -C /etc/systemd . -C /etc/network . -C /etc/docker . 2>/dev/null || true" 2>/dev/null; then
if ssh_with_password "$host" "$user" "test -f /tmp/system_configs_backup.tar.gz" 2>/dev/null; then
# Copy from host to raspberrypi via local machine
scp "$user@$host:/tmp/system_configs_backup.tar.gz" "/tmp/system_configs_backup_temp.tar.gz" 2>/dev/null || true
if [[ -f "/tmp/system_configs_backup_temp.tar.gz" ]]; then
rsync -avz "/tmp/system_configs_backup_temp.tar.gz" "jon@raspberrypi:$backup_path"
local size=$(stat -c%s "/tmp/system_configs_backup_temp.tar.gz" 2>/dev/null || echo "0")
log "✅ System config backup created: $backup_path ($size bytes)"
rm -f "/tmp/system_configs_backup_temp.tar.gz" 2>/dev/null || true
ssh_with_password "$host" "$user" "rm -f /tmp/system_configs_backup.tar.gz" 2>/dev/null || true
((COMPLETED_TASKS["system_configs"]++))
show_progress "system_configs"
else
log "⚠️ Failed to copy system config backup from $host"
fi
else
log "⚠️ Failed to create system config backup for $host"
fi
else
log "⚠️ Failed to connect to $host for system config backup"
fi
done < "$DISCOVERY_DIR/all_hosts.txt"
fi
}
# Create backup manifest
create_backup_manifest() {
log "=== CREATING BACKUP MANIFEST ==="
local manifest_file="$BACKUP_DIR/backup_manifest_${BACKUP_TIMESTAMP}.json"
cat > "/tmp/manifest.json" << EOF
{
"backup_timestamp": "$BACKUP_TIMESTAMP",
"backup_directory": "$BACKUP_DIR",
"total_size_bytes": $(ssh jon@raspberrypi "du -sb $BACKUP_DIR" | awk '{print $1}'),
"files": [
EOF
# Add all backup files to manifest
ssh jon@raspberrypi "find $BACKUP_DIR -name '*.tar.gz' -o -name '*.sql' | sort" | while read -r file; do
local filename=$(basename "$file")
local size=$(ssh jon@raspberrypi "stat -c%s '$file'" 2>/dev/null || echo "0")
local checksum=$(ssh jon@raspberrypi "sha256sum '$file'" 2>/dev/null | awk '{print $1}' || echo "")
cat >> "/tmp/manifest.json" << EOF
{
"filename": "$filename",
"path": "$file",
"size_bytes": $size,
"checksum": "$checksum"
},
EOF
done
# Remove trailing comma and close JSON
sed -i '$ s/,$//' "/tmp/manifest.json"
cat >> "/tmp/manifest.json" << EOF
],
"completion_time": "$(date -Iseconds)",
"total_tasks_completed": $((COMPLETED_TASKS["databases"] + COMPLETED_TASKS["volumes"] + COMPLETED_TASKS["configs"] + COMPLETED_TASKS["secrets"] + COMPLETED_TASKS["user_data"] + COMPLETED_TASKS["system_configs"]))
}
EOF
rsync -avz "/tmp/manifest.json" "jon@raspberrypi:$manifest_file"
log "✅ Backup manifest created: $manifest_file"
}
# Verify backup completeness
verify_backup_completeness() {
log "=== VERIFYING BACKUP COMPLETENESS ==="
local total_files=$(ssh jon@raspberrypi "find $BACKUP_DIR -name '*.tar.gz' -o -name '*.sql' | wc -l")
local total_size=$(ssh jon@raspberrypi "du -sh $BACKUP_DIR" | awk '{print $1}')
log "📊 Backup Summary:"
log " Total files: $total_files"
log " Total size: $total_size"
log " Backup location: $BACKUP_DIR"
# Check for critical components
local critical_missing=0
if ! ssh jon@raspberrypi "test -d '$BACKUP_DIR/configurations'" 2>/dev/null; then
log "❌ Missing: configurations"
((critical_missing++))
fi
if ! ssh jon@raspberrypi "test -d '$BACKUP_DIR/secrets'" 2>/dev/null; then
log "❌ Missing: secrets"
((critical_missing++))
fi
if ! ssh jon@raspberrypi "test -d '$BACKUP_DIR/user_data'" 2>/dev/null; then
log "❌ Missing: user_data"
((critical_missing++))
fi
if [[ $critical_missing -eq 0 ]]; then
log "✅ Backup verification passed - all critical components present"
else
log "⚠️ Backup verification warning - $critical_missing critical components missing"
fi
}
# Main backup function
main() {
if [[ -n "$RESUME_BACKUP_DIR" ]]; then
log "🔄 RESUMING COMPREHENSIVE PRE-MIGRATION BACKUP"
log "Resume directory: $RESUME_BACKUP_DIR"
else
log "=== COMPREHENSIVE PRE-MIGRATION BACKUP STARTED ==="
log "Timestamp: $BACKUP_TIMESTAMP"
log "Backup directory: $BACKUP_DIR"
fi
log "Password file: $PASSWORD_FILE"
log "Discovery directory: $DISCOVERY_DIR"
log "Progress file: $PROGRESS_FILE"
# Verify discovery results exist
if [[ ! -d "$DISCOVERY_DIR" ]]; then
log "ERROR: Discovery results not found. Run discovery script first."
exit 1
fi
# Initialize or load progress
if [[ -n "$RESUME_BACKUP_DIR" ]]; then
load_progress
else
# Create backup directory on raspberrypi
log "Creating backup directory on raspberrypi..."
ssh jon@raspberrypi "mkdir -p $BACKUP_DIR"
init_progress
fi
# Show initial progress
log "📊 Initial Progress:"
show_progress "databases"
show_progress "volumes"
show_progress "configs"
show_progress "secrets"
show_progress "user_data"
show_progress "system_configs"
# Backup all components
backup_all_databases
backup_all_docker_volumes
backup_all_configurations
backup_all_secrets
backup_all_user_data
backup_all_system_configs
# Create backup manifest
create_backup_manifest
# Verify backup completeness
verify_backup_completeness
log "=== BACKUP COMPLETE ==="
log "Backup location: $BACKUP_DIR"
log "Log file: $LOG_FILE"
log "Progress file: $PROGRESS_FILE"
# Show final progress
log "📊 Final Progress:"
show_progress "databases"
show_progress "volumes"
show_progress "configs"
show_progress "secrets"
show_progress "user_data"
show_progress "system_configs"
}
# Run main function
main "$@"