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
545 lines
23 KiB
Bash
Executable File
545 lines
23 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Automated Comprehensive Backup Target Discovery Script
|
|
# Uses password file to avoid repeated SSH password prompts
|
|
# Discovers 100% of what needs to be backed up across the entire infrastructure
|
|
|
|
set -euo pipefail
|
|
|
|
# Configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
DISCOVERY_TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
|
DISCOVERY_DIR="$PROJECT_ROOT/comprehensive_discovery_results"
|
|
LOG_FILE="$PROJECT_ROOT/logs/discovery_${DISCOVERY_TIMESTAMP}.log"
|
|
PASSWORD_FILE="$PROJECT_ROOT/secrets/ssh_passwords.env"
|
|
|
|
# Create directories
|
|
mkdir -p "$DISCOVERY_DIR" "$(dirname "$LOG_FILE")"
|
|
|
|
# Load passwords
|
|
if [[ -f "$PASSWORD_FILE" ]]; then
|
|
source "$PASSWORD_FILE"
|
|
else
|
|
echo "Error: Password file not found: $PASSWORD_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
# Logging function
|
|
log() {
|
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Error handling
|
|
cleanup() {
|
|
log "Cleaning up temporary files..."
|
|
rm -f /tmp/discovery_*.txt /tmp/docker_*.json /tmp/volume_*.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:-}"
|
|
;;
|
|
*)
|
|
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
|
|
}
|
|
|
|
# Main discovery function
|
|
main() {
|
|
log "=== AUTOMATED COMPREHENSIVE BACKUP TARGET DISCOVERY STARTED ==="
|
|
log "Timestamp: $DISCOVERY_TIMESTAMP"
|
|
log "Discovery directory: $DISCOVERY_DIR"
|
|
log "Password file: $PASSWORD_FILE"
|
|
|
|
# Discover all hosts in the infrastructure
|
|
discover_hosts
|
|
|
|
# Discover all Docker environments
|
|
discover_docker_environments
|
|
|
|
# Discover all systemd services (native services)
|
|
discover_systemd_services
|
|
|
|
# Discover all databases
|
|
discover_databases
|
|
|
|
# Discover all volumes and persistent data
|
|
discover_volumes
|
|
|
|
# Discover all configuration files
|
|
discover_configurations
|
|
|
|
# Discover all secrets and sensitive data
|
|
discover_secrets
|
|
|
|
# Discover all network configurations
|
|
discover_network_configs
|
|
|
|
# Discover all user data and applications
|
|
discover_user_data
|
|
|
|
# Discover all application-specific data
|
|
discover_application_data
|
|
|
|
# Generate comprehensive summary
|
|
generate_discovery_summary
|
|
|
|
log "=== DISCOVERY COMPLETE ==="
|
|
log "Results saved to: $DISCOVERY_DIR"
|
|
}
|
|
|
|
# Discover all hosts in the infrastructure
|
|
discover_hosts() {
|
|
log "=== DISCOVERING ALL HOSTS ==="
|
|
|
|
# Create a list of known hosts with their correct usernames from inventory
|
|
cat > "$DISCOVERY_DIR/all_hosts.txt" << 'EOF'
|
|
fedora:jonathan
|
|
omvbackup:jon
|
|
lenovo:jonathan
|
|
lenovo420:jon
|
|
omv800:root
|
|
surface:jon
|
|
audrey:jon
|
|
raspberrypi:jon
|
|
EOF
|
|
|
|
# Check connectivity to each host
|
|
while IFS=: read -r host user; do
|
|
if [[ -n "$host" && -n "$user" ]]; then
|
|
log "Checking connectivity to $host (user: $user)..."
|
|
if ping -c 1 -W 2 "$host" >/dev/null 2>&1; then
|
|
echo "$host:$user:ONLINE" >> "$DISCOVERY_DIR/host_status.txt"
|
|
else
|
|
echo "$host:$user:OFFLINE" >> "$DISCOVERY_DIR/host_status.txt"
|
|
fi
|
|
fi
|
|
done < "$DISCOVERY_DIR/all_hosts.txt"
|
|
|
|
# Also backup the inventory file
|
|
if [[ -f "$PROJECT_ROOT/inventory.ini" ]]; then
|
|
cp "$PROJECT_ROOT/inventory.ini" "$DISCOVERY_DIR/inventory_backup.txt"
|
|
fi
|
|
}
|
|
|
|
# Discover all Docker environments
|
|
discover_docker_environments() {
|
|
log "=== DISCOVERING DOCKER ENVIRONMENTS ==="
|
|
|
|
# Check each host for Docker
|
|
while IFS=: read -r host user; do
|
|
if [[ -n "$host" && -n "$user" ]]; then
|
|
log "Checking Docker on $host (user: $user)..."
|
|
|
|
# Check if Docker is running
|
|
if ssh_with_password "$host" "$user" "docker --version" 2>/dev/null; then
|
|
echo "$host:$user:DOCKER_AVAILABLE" >> "$DISCOVERY_DIR/docker_hosts.txt"
|
|
|
|
# Get Docker info
|
|
ssh_with_password "$host" "$user" "docker info" > "$DISCOVERY_DIR/docker_info_${host}.txt" 2>/dev/null || true
|
|
|
|
# Get all containers
|
|
ssh_with_password "$host" "$user" "docker ps -a --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'" > "$DISCOVERY_DIR/containers_${host}.txt" 2>/dev/null || true
|
|
|
|
# Get all images
|
|
ssh_with_password "$host" "$user" "docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}'" > "$DISCOVERY_DIR/images_${host}.txt" 2>/dev/null || true
|
|
|
|
# Get all networks
|
|
ssh_with_password "$host" "$user" "docker network ls" > "$DISCOVERY_DIR/networks_${host}.txt" 2>/dev/null || true
|
|
|
|
# Get all volumes
|
|
ssh_with_password "$host" "$user" "docker volume ls" > "$DISCOVERY_DIR/volumes_${host}.txt" 2>/dev/null || true
|
|
|
|
else
|
|
echo "$host:$user:NO_DOCKER" >> "$DISCOVERY_DIR/docker_hosts.txt"
|
|
fi
|
|
fi
|
|
done < "$DISCOVERY_DIR/all_hosts.txt"
|
|
}
|
|
|
|
# Discover all systemd services (native services)
|
|
discover_systemd_services() {
|
|
log "=== DISCOVERING SYSTEMD SERVICES ==="
|
|
|
|
# Check each host for systemd services
|
|
while IFS=: read -r host user; do
|
|
if [[ -n "$host" && -n "$user" ]]; then
|
|
log "Checking systemd services on $host (user: $user)..."
|
|
|
|
# Get active services
|
|
ssh_with_password "$host" "$user" "systemctl list-units --type=service --state=running --full --no-pager" > "$DISCOVERY_DIR/active_services_${host}.txt" 2>/dev/null || true
|
|
|
|
# Get service descriptions
|
|
ssh_with_password "$host" "$user" "systemctl list-units --type=service --full --no-pager" > "$DISCOVERY_DIR/service_descriptions_${host}.txt" 2>/dev/null || true
|
|
|
|
# Get service dependencies
|
|
ssh_with_password "$host" "$user" "systemctl list-dependencies --type=service --full --no-pager" > "$DISCOVERY_DIR/service_dependencies_${host}.txt" 2>/dev/null || true
|
|
fi
|
|
done < "$DISCOVERY_DIR/all_hosts.txt"
|
|
}
|
|
|
|
# Discover all databases
|
|
discover_databases() {
|
|
log "=== DISCOVERING ALL DATABASES ==="
|
|
|
|
# Check each Docker host for databases
|
|
while IFS=: read -r host user status; do
|
|
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
|
|
log "Discovering databases on $host (user: $user)..."
|
|
|
|
# Get containers that might be databases (expanded list based on documentation)
|
|
ssh_with_password "$host" "$user" "docker ps --format '{{.Names}} {{.Image}}' | grep -iE '(postgres|mysql|mariadb|redis|mongodb|sqlite|vector|valkey)'" > "$DISCOVERY_DIR/databases_${host}.txt" 2>/dev/null || true
|
|
|
|
# For each database container, get detailed info
|
|
while IFS= read -r db_line; do
|
|
if [[ -n "$db_line" ]]; then
|
|
container_name=$(echo "$db_line" | awk '{print $1}')
|
|
image=$(echo "$db_line" | awk '{print $2}')
|
|
|
|
log "Analyzing database container: $container_name ($image) on $host"
|
|
|
|
# Get environment variables
|
|
ssh_with_password "$host" "$user" "docker inspect $container_name | jq '.[0].Config.Env[]' -r" > "$DISCOVERY_DIR/db_env_${host}_${container_name}.txt" 2>/dev/null || true
|
|
|
|
# Get volume mounts
|
|
ssh_with_password "$host" "$user" "docker inspect $container_name | jq '.[0].Mounts[] | {Source: .Source, Destination: .Destination, Type: .Type}'" > "$DISCOVERY_DIR/db_mounts_${host}_${container_name}.json" 2>/dev/null || true
|
|
|
|
# Get database type and version
|
|
echo "Container: $container_name" > "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt"
|
|
echo "Image: $image" >> "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt"
|
|
echo "Host: $host" >> "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt"
|
|
|
|
# Try to get database version
|
|
if [[ "$image" == *"postgres"* ]] || [[ "$image" == *"pgvector"* ]]; then
|
|
ssh_with_password "$host" "$user" "docker exec $container_name psql --version" >> "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt" 2>/dev/null || true
|
|
elif [[ "$image" == *"mysql"* ]] || [[ "$image" == *"mariadb"* ]]; then
|
|
ssh_with_password "$host" "$user" "docker exec $container_name mysql --version" >> "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt" 2>/dev/null || true
|
|
elif [[ "$image" == *"redis"* ]] || [[ "$image" == *"valkey"* ]]; then
|
|
ssh_with_password "$host" "$user" "docker exec $container_name redis-server --version" >> "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt" 2>/dev/null || true
|
|
elif [[ "$image" == *"mongo"* ]]; then
|
|
ssh_with_password "$host" "$user" "docker exec $container_name mongod --version" >> "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
done < "$DISCOVERY_DIR/databases_${host}.txt"
|
|
fi
|
|
done < "$DISCOVERY_DIR/docker_hosts.txt"
|
|
}
|
|
|
|
# Discover all volumes and persistent data
|
|
discover_volumes() {
|
|
log "=== DISCOVERING ALL VOLUMES AND PERSISTENT DATA ==="
|
|
|
|
# Check each Docker host for volumes
|
|
while IFS=: read -r host user status; do
|
|
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
|
|
log "Discovering volumes on $host (user: $user)..."
|
|
|
|
# Get all Docker volumes with details
|
|
ssh_with_password "$host" "$user" "docker volume ls -q | xargs -I {} docker volume inspect {}" > "$DISCOVERY_DIR/volume_details_${host}.json" 2>/dev/null || true
|
|
|
|
# Get bind mounts from all containers
|
|
ssh_with_password "$host" "$user" "docker ps -q | xargs -I {} docker inspect {} | jq '.[] | {Name: .Name, Mounts: .Mounts}'" > "$DISCOVERY_DIR/bind_mounts_${host}.json" 2>/dev/null || true
|
|
|
|
# Check for important directories that might contain data (expanded based on documentation)
|
|
ssh_with_password "$host" "$user" "find /opt /var/lib /home /root /srv -name '*.db' -o -name '*.sqlite' -o -name 'data' -o -name 'config' -o -name 'nextcloud' -o -name 'immich' -o -name 'joplin' -o -name 'jellyfin' -o -name 'homeassistant' -o -name 'paperless' 2>/dev/null | head -100" > "$DISCOVERY_DIR/important_dirs_${host}.txt" 2>/dev/null || true
|
|
|
|
# Check for mergerfs pools (OMV800 specific)
|
|
if [[ "$host" == "omv800" ]]; then
|
|
ssh_with_password "$host" "$user" "find /srv/mergerfs -type d 2>/dev/null" > "$DISCOVERY_DIR/mergerfs_pools_${host}.txt" 2>/dev/null || true
|
|
ssh_with_password "$host" "$user" "df -h /srv/mergerfs/* 2>/dev/null" > "$DISCOVERY_DIR/mergerfs_usage_${host}.txt" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
done < "$DISCOVERY_DIR/docker_hosts.txt"
|
|
}
|
|
|
|
# Discover all configuration files
|
|
discover_configurations() {
|
|
log "=== DISCOVERING ALL CONFIGURATIONS ==="
|
|
|
|
# Local configurations
|
|
log "Discovering local configurations..."
|
|
|
|
# Docker Compose files
|
|
find "$PROJECT_ROOT" -name "*.yml" -o -name "*.yaml" -o -name "docker-compose*" > "$DISCOVERY_DIR/local_configs.txt"
|
|
|
|
# Environment files
|
|
find "$PROJECT_ROOT" -name "*.env" -o -name ".env*" >> "$DISCOVERY_DIR/local_configs.txt"
|
|
|
|
# Configuration directories
|
|
find "$PROJECT_ROOT" -type d -name "config*" -o -name "conf*" -o -name "etc*" >> "$DISCOVERY_DIR/local_configs.txt"
|
|
|
|
# Check each host for configurations
|
|
while IFS=: read -r host user status; do
|
|
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
|
|
log "Discovering configurations on $host (user: $user)..."
|
|
|
|
# Find configuration files
|
|
ssh_with_password "$host" "$user" "find /etc /opt /var/lib -name '*.conf' -o -name '*.yml' -o -name '*.yaml' -o -name '*.env' 2>/dev/null | head -100" > "$DISCOVERY_DIR/configs_${host}.txt" 2>/dev/null || true
|
|
|
|
# Get Docker Compose files
|
|
ssh_with_password "$host" "$user" "find /opt /root /home -name 'docker-compose*.yml' -o -name '*.stack.yml' 2>/dev/null" > "$DISCOVERY_DIR/compose_files_${host}.txt" 2>/dev/null || true
|
|
fi
|
|
done < "$DISCOVERY_DIR/docker_hosts.txt"
|
|
}
|
|
|
|
# Discover all secrets and sensitive data
|
|
discover_secrets() {
|
|
log "=== DISCOVERING ALL SECRETS AND SENSITIVE DATA ==="
|
|
|
|
# Local secrets
|
|
if [[ -d "$PROJECT_ROOT/secrets" ]]; then
|
|
log "Discovering local secrets..."
|
|
find "$PROJECT_ROOT/secrets" -type f > "$DISCOVERY_DIR/local_secrets.txt"
|
|
|
|
# Get secrets mapping
|
|
if [[ -f "$PROJECT_ROOT/secrets/docker-secrets-mapping.yaml" ]]; then
|
|
cp "$PROJECT_ROOT/secrets/docker-secrets-mapping.yaml" "$DISCOVERY_DIR/"
|
|
fi
|
|
fi
|
|
|
|
# Check each host for secrets
|
|
while IFS=: read -r host user status; do
|
|
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
|
|
log "Discovering secrets on $host (user: $user)..."
|
|
|
|
# Check for Docker secrets
|
|
ssh_with_password "$host" "$user" "docker secret ls" > "$DISCOVERY_DIR/secrets_${host}.txt" 2>/dev/null || true
|
|
|
|
# Check for environment files with secrets
|
|
ssh_with_password "$host" "$user" "find /opt /root /home -name '.env*' -o -name '*secret*' -o -name '*password*' 2>/dev/null" > "$DISCOVERY_DIR/secret_files_${host}.txt" 2>/dev/null || true
|
|
|
|
# Check for SSL certificates
|
|
ssh_with_password "$host" "$user" "find /etc /opt -name '*.crt' -o -name '*.key' -o -name '*.pem' 2>/dev/null" > "$DISCOVERY_DIR/ssl_files_${host}.txt" 2>/dev/null || true
|
|
fi
|
|
done < "$DISCOVERY_DIR/docker_hosts.txt"
|
|
}
|
|
|
|
# Discover all network configurations
|
|
discover_network_configs() {
|
|
log "=== DISCOVERING ALL NETWORK CONFIGURATIONS ==="
|
|
|
|
# Local network config
|
|
log "Discovering local network configuration..."
|
|
ip route > "$DISCOVERY_DIR/local_routes.txt"
|
|
ip addr > "$DISCOVERY_DIR/local_interfaces.txt"
|
|
cat /etc/hosts > "$DISCOVERY_DIR/local_hosts.txt"
|
|
|
|
# Check each host for network config
|
|
while IFS=: read -r host user status; do
|
|
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
|
|
log "Discovering network configuration on $host (user: $user)..."
|
|
|
|
# Network interfaces
|
|
ssh_with_password "$host" "$user" "ip addr" > "$DISCOVERY_DIR/interfaces_${host}.txt" 2>/dev/null || true
|
|
|
|
# Routing table
|
|
ssh_with_password "$host" "$user" "ip route" > "$DISCOVERY_DIR/routes_${host}.txt" 2>/dev/null || true
|
|
|
|
# Hosts file
|
|
ssh_with_password "$host" "$user" "cat /etc/hosts" > "$DISCOVERY_DIR/hosts_${host}.txt" 2>/dev/null || true
|
|
|
|
# Docker networks
|
|
ssh_with_password "$host" "$user" "docker network ls" > "$DISCOVERY_DIR/docker_networks_${host}.txt" 2>/dev/null || true
|
|
fi
|
|
done < "$DISCOVERY_DIR/docker_hosts.txt"
|
|
}
|
|
|
|
# Discover all user data and applications
|
|
discover_user_data() {
|
|
log "=== DISCOVERING ALL USER DATA AND APPLICATIONS ==="
|
|
|
|
# Check each host for user data
|
|
while IFS=: read -r host user status; do
|
|
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
|
|
log "Discovering user data on $host (user: $user)..."
|
|
|
|
# Check for common application data directories
|
|
ssh_with_password "$host" "$user" "find /opt /var/lib /home -type d -name '*data*' -o -name '*app*' -o -name '*user*' 2>/dev/null | head -50" > "$DISCOVERY_DIR/app_dirs_${host}.txt" 2>/dev/null || true
|
|
|
|
# Check for specific application directories
|
|
ssh_with_password "$host" "$user" "find /opt /var/lib -name '*nextcloud*' -o -name '*immich*' -o -name '*joplin*' -o -name '*photoprism*' 2>/dev/null" > "$DISCOVERY_DIR/specific_apps_${host}.txt" 2>/dev/null || true
|
|
|
|
# Check for media files
|
|
ssh_with_password "$host" "$user" "find /opt /var/lib -type d -name '*media*' -o -name '*photos*' -o -name '*videos*' -o -name '*music*' 2>/dev/null" > "$DISCOVERY_DIR/media_dirs_${host}.txt" 2>/dev/null || true
|
|
fi
|
|
done < "$DISCOVERY_DIR/docker_hosts.txt"
|
|
}
|
|
|
|
# Discover all application-specific data
|
|
discover_application_data() {
|
|
log "=== DISCOVERING ALL APPLICATION-SPECIFIC DATA ==="
|
|
|
|
# Check each host for application-specific data
|
|
while IFS=: read -r host user status; do
|
|
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
|
|
log "Discovering application-specific data on $host (user: $user)..."
|
|
|
|
# Check for Nextcloud data
|
|
ssh_with_password "$host" "$user" "find /opt /var/lib -name 'nextcloud' -type d -o -name 'nextcloud.db' 2>/dev/null" > "$DISCOVERY_DIR/nextcloud_data_${host}.txt" 2>/dev/null || true
|
|
|
|
# Check for Immich data
|
|
ssh_with_password "$host" "$user" "find /opt /var/lib -name 'immich' -type d -o -name 'immich.db' 2>/dev/null" > "$DISCOVERY_DIR/immich_data_${host}.txt" 2>/dev/null || true
|
|
|
|
# Check for Joplin data
|
|
ssh_with_password "$host" "$user" "find /opt /var/lib -name 'joplin' -type d -o -name 'joplin.db' 2>/dev/null" > "$DISCOVERY_DIR/joplin_data_${host}.txt" 2>/dev/null || true
|
|
|
|
# Check for PhotoPrism data
|
|
ssh_with_password "$host" "$user" "find /opt /var/lib -name 'photoprism' -type d -o -name 'photoprism.db' 2>/dev/null" > "$DISCOVERY_DIR/photoprism_data_${host}.txt" 2>/dev/null || true
|
|
|
|
# Check for specific application data directories
|
|
ssh_with_password "$host" "$user" "find /opt /var/lib -name '*nextcloud*' -o -name '*immich*' -o -name '*joplin*' -o -name '*photoprism*' 2>/dev/null" > "$DISCOVERY_DIR/specific_apps_${host}.txt" 2>/dev/null || true
|
|
|
|
# Check for media files
|
|
ssh_with_password "$host" "$user" "find /opt /var/lib -type d -name '*media*' -o -name '*photos*' -o -name '*videos*' -o -name '*music*' 2>/dev/null" > "$DISCOVERY_DIR/media_dirs_${host}.txt" 2>/dev/null || true
|
|
fi
|
|
done < "$DISCOVERY_DIR/docker_hosts.txt"
|
|
}
|
|
|
|
# Generate comprehensive summary
|
|
generate_discovery_summary() {
|
|
log "=== GENERATING DISCOVERY SUMMARY ==="
|
|
|
|
cat > "$DISCOVERY_DIR/DISCOVERY_SUMMARY.md" << EOF
|
|
# Comprehensive Backup Target Discovery Summary
|
|
|
|
**Discovery Timestamp:** $DISCOVERY_TIMESTAMP
|
|
**Discovery Directory:** $DISCOVERY_DIR
|
|
|
|
## Hosts Discovered
|
|
$(cat "$DISCOVERY_DIR/host_status.txt" 2>/dev/null || echo "No host status found")
|
|
|
|
## Docker Environments
|
|
$(cat "$DISCOVERY_DIR/docker_hosts.txt" 2>/dev/null || echo "No Docker hosts found")
|
|
|
|
## Systemd Services
|
|
$(for file in "$DISCOVERY_DIR"/active_services_*.txt; do
|
|
if [[ -f "$file" ]]; then
|
|
host=$(basename "$file" | sed 's/active_services_//;s/.txt//')
|
|
echo "### $host"
|
|
cat "$file" | sed 's/^/ - /'
|
|
echo
|
|
fi
|
|
done)
|
|
|
|
## Databases Found
|
|
$(for file in "$DISCOVERY_DIR"/databases_*.txt; do
|
|
if [[ -f "$file" ]]; then
|
|
host=$(basename "$file" | sed 's/databases_//;s/.txt//')
|
|
echo "### $host"
|
|
cat "$file" | sed 's/^/ - /'
|
|
echo
|
|
fi
|
|
done)
|
|
|
|
## Volumes and Persistent Data
|
|
$(for file in "$DISCOVERY_DIR"/volumes_*.txt; do
|
|
if [[ -f "$file" ]]; then
|
|
host=$(basename "$file" | sed 's/volumes_//;s/.txt//')
|
|
echo "### $host"
|
|
cat "$file" | sed 's/^/ - /'
|
|
echo
|
|
fi
|
|
done)
|
|
|
|
## Configuration Files
|
|
- Local configurations: $(wc -l < "$DISCOVERY_DIR/local_configs.txt" 2>/dev/null || echo "0")
|
|
- Environment files: $(grep -c "\.env" "$DISCOVERY_DIR/local_configs.txt" 2>/dev/null || echo "0")
|
|
|
|
## Secrets and SSL Certificates
|
|
- Local secrets: $(wc -l < "$DISCOVERY_DIR/local_secrets.txt" 2>/dev/null || echo "0")
|
|
- SSL files across hosts: $(find "$DISCOVERY_DIR" -name "*ssl_files*.txt" | wc -l)
|
|
|
|
## Network Configurations
|
|
- Local network config captured
|
|
- Network configs for $(find "$DISCOVERY_DIR" -name "*interfaces*.txt" | wc -l) hosts
|
|
|
|
## User Data and Applications
|
|
$(for file in "$DISCOVERY_DIR"/specific_apps_*.txt; do
|
|
if [[ -f "$file" ]]; then
|
|
host=$(basename "$file" | sed 's/specific_apps_//;s/.txt//')
|
|
echo "### $host"
|
|
cat "$file" | sed 's/^/ - /'
|
|
echo
|
|
fi
|
|
done)
|
|
|
|
## Application-Specific Data
|
|
$(for file in "$DISCOVERY_DIR"/nextcloud_data_*.txt "$DISCOVERY_DIR"/immich_data_*.txt "$DISCOVERY_DIR"/joplin_data_*.txt "$DISCOVERY_DIR"/photoprism_data_*.txt; do
|
|
if [[ -f "$file" ]]; then
|
|
host=$(basename "$file" | sed 's/nextcloud_data_//;s/immich_data_//;s/joplin_data_//;s/photoprism_data_//;s/.txt//')
|
|
echo "### $host"
|
|
cat "$file" | sed 's/^/ - /'
|
|
echo
|
|
fi
|
|
done)
|
|
|
|
## Backup Requirements Summary
|
|
|
|
### Critical Data to Backup:
|
|
1. **Databases**: All PostgreSQL, MariaDB, Redis instances
|
|
2. **Volumes**: All Docker volumes and bind mounts
|
|
3. **Configurations**: All .env files, docker-compose files, config directories
|
|
4. **Secrets**: All SSL certificates, API keys, passwords
|
|
5. **User Data**: Nextcloud, Immich, Joplin, PhotoPrism data
|
|
6. **Network Configs**: Routing, interfaces, Docker networks
|
|
7. **Documentation**: All infrastructure documentation and scripts
|
|
|
|
### Estimated Backup Size:
|
|
- Configuration files: ~10-50MB
|
|
- Database dumps: ~100MB-1GB (depending on data)
|
|
- User data: ~1-10GB (depending on media)
|
|
- Total estimated: ~1-15GB
|
|
|
|
## Next Steps:
|
|
1. Review this discovery summary
|
|
2. Create comprehensive backup script based on discovered targets
|
|
3. Test backup process on non-critical data first
|
|
4. Execute full backup before migration
|
|
EOF
|
|
|
|
log "Discovery summary generated: $DISCOVERY_DIR/DISCOVERY_SUMMARY.md"
|
|
}
|
|
|
|
# Execute main function
|
|
main "$@"
|