added full backup restore
parent
0b3465f79e
commit
b61d38b946
|
@ -0,0 +1,512 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Exit on errors and enable comprehensive error tracing
|
||||
set -e
|
||||
trap 'log "CRITICAL ERROR: Full backup session restoration failed at line $LINENO. Aborting all operations."' ERR
|
||||
|
||||
# Validate input parameters
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <backup_session_tag>"
|
||||
echo ""
|
||||
echo "Example: $0 manual-backup-2024-01-15_10-30-45"
|
||||
echo " $0 automated-backup-2024-01-15_02-00-30"
|
||||
echo ""
|
||||
echo "Use view_snapshots.sh to find available backup session tags."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Assign input to variables
|
||||
BACKUP_SESSION_TAG="$1"
|
||||
RESTIC_PASSWORD_FILE="/etc/restic-password"
|
||||
RESTIC_REPOSITORY="/mnt/backups"
|
||||
LOG_DIR="/home/jelastic/mb-backups/logs/restore"
|
||||
WP_CONFIG="/var/www/webroot/ROOT/wp-config.php"
|
||||
TEMP_VALIDATION_DIR="/tmp/full_backup_validation_$$"
|
||||
|
||||
# Required component tags for a complete full backup
|
||||
REQUIRED_CORE_TAG="core_files"
|
||||
REQUIRED_MEDIA_TAG="media_themes"
|
||||
REQUIRED_DB_TAG="wordpress_db"
|
||||
|
||||
# Ensure the log directory exists
|
||||
mkdir -p "$LOG_DIR"
|
||||
LOG_FILE="${LOG_DIR}/restore_full_backup_$(date +'%Y-%m-%d_%H-%M-%S').log"
|
||||
|
||||
# Create temporary validation directory
|
||||
mkdir -p "$TEMP_VALIDATION_DIR"
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
log "Cleaning up temporary files..."
|
||||
rm -rf "$TEMP_VALIDATION_DIR" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Set cleanup trap
|
||||
trap 'cleanup; log "CRITICAL ERROR: Full backup session restoration failed. Cleanup completed."' ERR
|
||||
trap 'cleanup' EXIT
|
||||
|
||||
# Logging function with enhanced formatting
|
||||
log() {
|
||||
local level="INFO"
|
||||
local message="$1"
|
||||
|
||||
if [[ "$message" == CRITICAL* ]] || [[ "$message" == ERROR* ]]; then
|
||||
level="ERROR"
|
||||
elif [[ "$message" == WARNING* ]]; then
|
||||
level="WARN"
|
||||
fi
|
||||
|
||||
echo "[$(date +'%Y-%m-%d %H:%M:%S')] [$level] $message" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Validate required dependencies
|
||||
validate_dependencies() {
|
||||
log "Validating system dependencies..."
|
||||
|
||||
for cmd in restic jq mysql; do
|
||||
if ! command -v $cmd >/dev/null 2>&1; then
|
||||
log "CRITICAL ERROR: Required command '$cmd' not found. Please install $cmd."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
log "All required dependencies validated successfully."
|
||||
}
|
||||
|
||||
# Set up Restic environment
|
||||
setup_restic_environment() {
|
||||
log "Setting up Restic environment..."
|
||||
|
||||
if [ ! -f "$RESTIC_PASSWORD_FILE" ]; then
|
||||
log "CRITICAL ERROR: Restic password file not found at $RESTIC_PASSWORD_FILE."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$RESTIC_REPOSITORY" ]; then
|
||||
log "CRITICAL ERROR: Restic repository not found at $RESTIC_REPOSITORY."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export RESTIC_PASSWORD=$(cat "$RESTIC_PASSWORD_FILE")
|
||||
export RESTIC_REPOSITORY="$RESTIC_REPOSITORY"
|
||||
|
||||
log "Restic environment configured successfully."
|
||||
}
|
||||
|
||||
# Remove stale locks from repository
|
||||
remove_stale_locks() {
|
||||
log "Checking for stale locks in repository..."
|
||||
|
||||
if restic list locks 2>/dev/null | grep -q "lock"; then
|
||||
log "WARNING: Stale locks detected. Attempting to unlock repository..."
|
||||
if restic unlock; then
|
||||
log "Repository unlocked successfully."
|
||||
else
|
||||
log "CRITICAL ERROR: Failed to remove stale locks from repository."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log "No stale locks found. Repository is clean."
|
||||
fi
|
||||
}
|
||||
|
||||
# Validate backup session tag format
|
||||
validate_session_tag_format() {
|
||||
log "Validating backup session tag format: '$BACKUP_SESSION_TAG'"
|
||||
|
||||
# Check if tag matches expected patterns
|
||||
if [[ ! "$BACKUP_SESSION_TAG" =~ ^(manual|automated)-backup-[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}$ ]]; then
|
||||
log "CRITICAL ERROR: Invalid backup session tag format."
|
||||
log "Expected format: manual-backup-YYYY-MM-DD_HH-MM-SS or automated-backup-YYYY-MM-DD_HH-MM-SS"
|
||||
log "Received: '$BACKUP_SESSION_TAG'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Backup session tag format is valid."
|
||||
}
|
||||
|
||||
# Retrieve and validate all snapshots for the session
|
||||
retrieve_session_snapshots() {
|
||||
log "Retrieving all snapshots for backup session: '$BACKUP_SESSION_TAG'"
|
||||
|
||||
# Get all snapshots with the session tag
|
||||
local snapshots_json
|
||||
snapshots_json=$(restic snapshots --tag "$BACKUP_SESSION_TAG" --json 2>/dev/null)
|
||||
|
||||
if [ -z "$snapshots_json" ] || [ "$snapshots_json" = "null" ] || [ "$snapshots_json" = "[]" ]; then
|
||||
log "CRITICAL ERROR: No snapshots found with backup session tag '$BACKUP_SESSION_TAG'."
|
||||
log "Use 'view_snapshots.sh all' to see available backup sessions."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Save snapshots data for validation
|
||||
echo "$snapshots_json" > "$TEMP_VALIDATION_DIR/session_snapshots.json"
|
||||
|
||||
# Count total snapshots in session
|
||||
local snapshot_count
|
||||
snapshot_count=$(echo "$snapshots_json" | jq '. | length')
|
||||
|
||||
log "Found $snapshot_count snapshots with session tag '$BACKUP_SESSION_TAG'."
|
||||
|
||||
# Validate we have exactly 3 snapshots (core, media, database)
|
||||
if [ "$snapshot_count" -ne 3 ]; then
|
||||
log "CRITICAL ERROR: Incomplete backup session detected."
|
||||
log "Expected exactly 3 snapshots (core_files, media_themes, wordpress_db), found $snapshot_count."
|
||||
log "This backup session is incomplete and cannot be safely restored."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Snapshot count validation passed: Found expected 3 snapshots."
|
||||
}
|
||||
|
||||
# Validate that all required components are present
|
||||
validate_backup_completeness() {
|
||||
log "Performing comprehensive backup completeness validation..."
|
||||
|
||||
local snapshots_json
|
||||
snapshots_json=$(cat "$TEMP_VALIDATION_DIR/session_snapshots.json")
|
||||
|
||||
# Extract all tags from all snapshots in the session
|
||||
local all_tags
|
||||
all_tags=$(echo "$snapshots_json" | jq -r '.[].tags[]' | sort | uniq)
|
||||
|
||||
# Check for required component tags
|
||||
local missing_components=()
|
||||
|
||||
if ! echo "$all_tags" | grep -q "^${REQUIRED_CORE_TAG}$"; then
|
||||
missing_components+=("core_files")
|
||||
fi
|
||||
|
||||
if ! echo "$all_tags" | grep -q "^${REQUIRED_MEDIA_TAG}$"; then
|
||||
missing_components+=("media_themes")
|
||||
fi
|
||||
|
||||
if ! echo "$all_tags" | grep -q "^${REQUIRED_DB_TAG}$"; then
|
||||
missing_components+=("wordpress_db")
|
||||
fi
|
||||
|
||||
# Report missing components
|
||||
if [ ${#missing_components[@]} -gt 0 ]; then
|
||||
log "CRITICAL ERROR: Backup session is missing required components:"
|
||||
for component in "${missing_components[@]}"; do
|
||||
log " - Missing: $component"
|
||||
done
|
||||
log "Cannot proceed with restoration of incomplete backup session."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Backup completeness validation passed: All required components present."
|
||||
}
|
||||
|
||||
# Extract and validate individual component snapshots
|
||||
extract_component_snapshots() {
|
||||
log "Extracting and validating individual component snapshots..."
|
||||
|
||||
local snapshots_json
|
||||
snapshots_json=$(cat "$TEMP_VALIDATION_DIR/session_snapshots.json")
|
||||
|
||||
# Extract core files snapshot
|
||||
CORE_SNAPSHOT=$(echo "$snapshots_json" | jq -r ".[] | select(.tags[] | contains(\"$REQUIRED_CORE_TAG\")) | .short_id")
|
||||
CORE_SNAPSHOT_FULL=$(echo "$snapshots_json" | jq -r ".[] | select(.tags[] | contains(\"$REQUIRED_CORE_TAG\"))")
|
||||
|
||||
# Extract media files snapshot
|
||||
MEDIA_SNAPSHOT=$(echo "$snapshots_json" | jq -r ".[] | select(.tags[] | contains(\"$REQUIRED_MEDIA_TAG\")) | .short_id")
|
||||
MEDIA_SNAPSHOT_FULL=$(echo "$snapshots_json" | jq -r ".[] | select(.tags[] | contains(\"$REQUIRED_MEDIA_TAG\"))")
|
||||
|
||||
# Extract database snapshot
|
||||
DB_SNAPSHOT=$(echo "$snapshots_json" | jq -r ".[] | select(.tags[] | contains(\"$REQUIRED_DB_TAG\")) | .short_id")
|
||||
DB_SNAPSHOT_FULL=$(echo "$snapshots_json" | jq -r ".[] | select(.tags[] | contains(\"$REQUIRED_DB_TAG\"))")
|
||||
|
||||
# Validate all snapshots were found
|
||||
if [ -z "$CORE_SNAPSHOT" ] || [ "$CORE_SNAPSHOT" = "null" ]; then
|
||||
log "CRITICAL ERROR: Core files snapshot not found in backup session."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$MEDIA_SNAPSHOT" ] || [ "$MEDIA_SNAPSHOT" = "null" ]; then
|
||||
log "CRITICAL ERROR: Media files snapshot not found in backup session."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$DB_SNAPSHOT" ] || [ "$DB_SNAPSHOT" = "null" ]; then
|
||||
log "CRITICAL ERROR: Database snapshot not found in backup session."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate snapshot IDs are unique (no duplicates)
|
||||
if [ "$CORE_SNAPSHOT" = "$MEDIA_SNAPSHOT" ] || [ "$CORE_SNAPSHOT" = "$DB_SNAPSHOT" ] || [ "$MEDIA_SNAPSHOT" = "$DB_SNAPSHOT" ]; then
|
||||
log "CRITICAL ERROR: Duplicate snapshot IDs detected. Backup session integrity compromised."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Component snapshot extraction successful:"
|
||||
log " - Core Files Snapshot: $CORE_SNAPSHOT"
|
||||
log " - Media Files Snapshot: $MEDIA_SNAPSHOT"
|
||||
log " - Database Snapshot: $DB_SNAPSHOT"
|
||||
}
|
||||
|
||||
# Validate session timestamp consistency
|
||||
validate_session_timestamp_consistency() {
|
||||
log "Validating backup session timestamp consistency..."
|
||||
|
||||
local snapshots_json
|
||||
snapshots_json=$(cat "$TEMP_VALIDATION_DIR/session_snapshots.json")
|
||||
|
||||
# Extract timestamps from all snapshots
|
||||
local timestamps
|
||||
timestamps=$(echo "$snapshots_json" | jq -r '.[].time' | sort | uniq)
|
||||
|
||||
# Count unique timestamps
|
||||
local timestamp_count
|
||||
timestamp_count=$(echo "$timestamps" | wc -l)
|
||||
|
||||
# Allow for small time differences (up to 30 minutes) for backup session
|
||||
local first_timestamp
|
||||
local last_timestamp
|
||||
first_timestamp=$(echo "$timestamps" | head -n1)
|
||||
last_timestamp=$(echo "$timestamps" | tail -n1)
|
||||
|
||||
# Convert to epoch for comparison
|
||||
local first_epoch
|
||||
local last_epoch
|
||||
first_epoch=$(date -d "$first_timestamp" +%s 2>/dev/null || echo "0")
|
||||
last_epoch=$(date -d "$last_timestamp" +%s 2>/dev/null || echo "0")
|
||||
|
||||
# Calculate time difference (30 minutes = 1800 seconds)
|
||||
local time_diff=$((last_epoch - first_epoch))
|
||||
|
||||
if [ $time_diff -gt 1800 ]; then
|
||||
log "WARNING: Large time difference detected between snapshots in session ($time_diff seconds)."
|
||||
log "First snapshot: $first_timestamp"
|
||||
log "Last snapshot: $last_timestamp"
|
||||
log "This may indicate snapshots from different backup sessions."
|
||||
|
||||
# Ask for confirmation in interactive mode
|
||||
if [ -t 0 ]; then
|
||||
echo "Do you want to continue with restoration? (yes/no): "
|
||||
read -r confirmation
|
||||
if [ "$confirmation" != "yes" ]; then
|
||||
log "Restoration cancelled by user due to timestamp inconsistency."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log "CRITICAL ERROR: Timestamp inconsistency detected in non-interactive mode. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log "Session timestamp consistency validation completed."
|
||||
}
|
||||
|
||||
# Extract database credentials from wp-config.php
|
||||
extract_db_credentials() {
|
||||
log "Extracting database credentials from WordPress configuration..."
|
||||
|
||||
if [ ! -f "$WP_CONFIG" ]; then
|
||||
log "CRITICAL ERROR: wp-config.php not found at $WP_CONFIG."
|
||||
log "Ensure WordPress is properly installed before restoration."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DB_NAME=$(awk -F"'" '/define\( *'"'"'DB_NAME'"'"'/{print $4}' "$WP_CONFIG")
|
||||
DB_USER=$(awk -F"'" '/define\( *'"'"'DB_USER'"'"'/{print $4}' "$WP_CONFIG")
|
||||
DB_PASSWORD=$(awk -F"'" '/define\( *'"'"'DB_PASSWORD'"'"'/{print $4}' "$WP_CONFIG")
|
||||
DB_HOST=$(awk -F"'" '/define\( *'"'"'DB_HOST'"'"'/{print $4}' "$WP_CONFIG")
|
||||
|
||||
# Set default DB_HOST if empty
|
||||
if [ -z "$DB_HOST" ]; then
|
||||
DB_HOST="localhost"
|
||||
fi
|
||||
|
||||
if [ -z "$DB_NAME" ] || [ -z "$DB_USER" ] || [ -z "$DB_PASSWORD" ]; then
|
||||
log "CRITICAL ERROR: Could not extract complete database credentials from wp-config.php."
|
||||
log "Extracted: DB_NAME='$DB_NAME', DB_USER='$DB_USER', DB_HOST='$DB_HOST'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Database credentials extracted successfully for database: $DB_NAME"
|
||||
}
|
||||
|
||||
# Test database connectivity before restoration
|
||||
test_database_connectivity() {
|
||||
log "Testing database connectivity before restoration..."
|
||||
|
||||
if ! mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" -e "SELECT 1;" >/dev/null 2>&1; then
|
||||
log "CRITICAL ERROR: Cannot connect to database."
|
||||
log "Please verify database credentials and ensure MySQL service is running."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Database connectivity test passed."
|
||||
}
|
||||
|
||||
# Create pre-restoration backup of current state
|
||||
create_pre_restoration_backup() {
|
||||
log "Creating pre-restoration backup of current state..."
|
||||
|
||||
local pre_backup_tag="pre-restore-$(date +'%Y-%m-%d_%H-%M-%S')"
|
||||
local pre_backup_dir="/tmp/pre_restore_backup_$$"
|
||||
|
||||
mkdir -p "$pre_backup_dir"
|
||||
|
||||
# Backup current database
|
||||
log "Backing up current database state..."
|
||||
if mysqldump -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" > "$pre_backup_dir/current_database.sql" 2>/dev/null; then
|
||||
log "Current database backed up successfully."
|
||||
else
|
||||
log "WARNING: Could not backup current database state."
|
||||
fi
|
||||
|
||||
# Create snapshot of current state
|
||||
if restic backup "$pre_backup_dir" --tag "$pre_backup_tag" --tag "pre-restoration-backup" >/dev/null 2>&1; then
|
||||
log "Pre-restoration backup created with tag: $pre_backup_tag"
|
||||
echo "$pre_backup_tag" > "$TEMP_VALIDATION_DIR/pre_restore_tag.txt"
|
||||
else
|
||||
log "WARNING: Could not create pre-restoration backup snapshot."
|
||||
fi
|
||||
|
||||
rm -rf "$pre_backup_dir"
|
||||
}
|
||||
|
||||
# Restore database component
|
||||
restore_database() {
|
||||
log "Starting database restoration from snapshot: $DB_SNAPSHOT"
|
||||
|
||||
# Get database snapshot details
|
||||
local db_snapshot_data
|
||||
db_snapshot_data=$(echo "$DB_SNAPSHOT_FULL")
|
||||
|
||||
local db_paths
|
||||
db_paths=$(echo "$db_snapshot_data" | jq -r '.paths[]' 2>/dev/null || echo "")
|
||||
|
||||
if [ -z "$db_paths" ]; then
|
||||
log "CRITICAL ERROR: Database snapshot contains no valid paths."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Restoring database from path: $db_paths"
|
||||
log "Target database: $DB_NAME on $DB_HOST"
|
||||
|
||||
# Restore database with error handling
|
||||
if restic dump "$DB_SNAPSHOT" "$db_paths" | mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME"; then
|
||||
log "Database restoration completed successfully."
|
||||
else
|
||||
log "CRITICAL ERROR: Database restoration failed."
|
||||
log "You may need to restore from the pre-restoration backup if available."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Restore core files component
|
||||
restore_core_files() {
|
||||
log "Starting core files restoration from snapshot: $CORE_SNAPSHOT"
|
||||
|
||||
local restore_target="/var/www/webroot/ROOT"
|
||||
|
||||
log "Restoring core files to: $restore_target"
|
||||
|
||||
if restic restore "$CORE_SNAPSHOT" --target "$restore_target"; then
|
||||
log "Core files restoration completed successfully."
|
||||
else
|
||||
log "CRITICAL ERROR: Core files restoration failed."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Restore media files component
|
||||
restore_media_files() {
|
||||
log "Starting media files restoration from snapshot: $MEDIA_SNAPSHOT"
|
||||
|
||||
local restore_target="/var/www/webroot/ROOT"
|
||||
|
||||
log "Restoring media files to: $restore_target"
|
||||
|
||||
if restic restore "$MEDIA_SNAPSHOT" --target "$restore_target"; then
|
||||
log "Media files restoration completed successfully."
|
||||
else
|
||||
log "CRITICAL ERROR: Media files restoration failed."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Verify restoration integrity
|
||||
verify_restoration_integrity() {
|
||||
log "Performing post-restoration integrity verification..."
|
||||
|
||||
# Check WordPress core files
|
||||
if [ ! -f "/var/www/webroot/ROOT/wp-config.php" ]; then
|
||||
log "WARNING: wp-config.php not found after restoration."
|
||||
fi
|
||||
|
||||
if [ ! -d "/var/www/webroot/ROOT/wp-content" ]; then
|
||||
log "WARNING: wp-content directory not found after restoration."
|
||||
fi
|
||||
|
||||
# Check database connectivity
|
||||
if mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" -e "SHOW TABLES;" "$DB_NAME" >/dev/null 2>&1; then
|
||||
log "Database connectivity verified after restoration."
|
||||
else
|
||||
log "WARNING: Database connectivity issues detected after restoration."
|
||||
fi
|
||||
|
||||
log "Restoration integrity verification completed."
|
||||
}
|
||||
|
||||
# Display restoration summary
|
||||
display_restoration_summary() {
|
||||
log "=== FULL BACKUP SESSION RESTORATION SUMMARY ==="
|
||||
log "Backup Session Tag: $BACKUP_SESSION_TAG"
|
||||
log "Components Restored:"
|
||||
log " - Database (Snapshot: $DB_SNAPSHOT)"
|
||||
log " - Core Files (Snapshot: $CORE_SNAPSHOT)"
|
||||
log " - Media Files (Snapshot: $MEDIA_SNAPSHOT)"
|
||||
log "Restoration completed at: $(date +'%Y-%m-%d %H:%M:%S')"
|
||||
|
||||
if [ -f "$TEMP_VALIDATION_DIR/pre_restore_tag.txt" ]; then
|
||||
local pre_restore_tag
|
||||
pre_restore_tag=$(cat "$TEMP_VALIDATION_DIR/pre_restore_tag.txt")
|
||||
log "Pre-restoration backup available with tag: $pre_restore_tag"
|
||||
fi
|
||||
|
||||
log "Full restoration log saved to: $LOG_FILE"
|
||||
log "=============================================="
|
||||
}
|
||||
|
||||
# Main execution flow
|
||||
main() {
|
||||
log "Starting full backup session restoration for: '$BACKUP_SESSION_TAG'"
|
||||
log "Process ID: $$"
|
||||
log "Log file: $LOG_FILE"
|
||||
|
||||
# Phase 1: Environment Setup and Validation
|
||||
validate_dependencies
|
||||
setup_restic_environment
|
||||
remove_stale_locks
|
||||
validate_session_tag_format
|
||||
|
||||
# Phase 2: Backup Session Discovery and Validation
|
||||
retrieve_session_snapshots
|
||||
validate_backup_completeness
|
||||
extract_component_snapshots
|
||||
validate_session_timestamp_consistency
|
||||
|
||||
# Phase 3: Pre-restoration Preparation
|
||||
extract_db_credentials
|
||||
test_database_connectivity
|
||||
create_pre_restoration_backup
|
||||
|
||||
# Phase 4: Restoration Execution (Order is critical)
|
||||
log "Beginning restoration sequence..."
|
||||
restore_database
|
||||
restore_core_files
|
||||
restore_media_files
|
||||
|
||||
# Phase 5: Post-restoration Verification
|
||||
verify_restoration_integrity
|
||||
display_restoration_summary
|
||||
|
||||
log "Full backup session restoration completed successfully!"
|
||||
}
|
||||
|
||||
# Execute main function
|
||||
main
|
|
@ -0,0 +1,414 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Configuration
|
||||
backupPath='/mnt/backups'
|
||||
password_file="/etc/restic-password"
|
||||
LOG_DIR="$HOME/mb-backups/logs"
|
||||
LOG_FILE="${LOG_DIR}/backup_sessions.log"
|
||||
ERROR_LOG_FILE="${LOG_DIR}/backup_sessions_error.log"
|
||||
|
||||
# Ensure log directory exists
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
# Logging function
|
||||
log_message() {
|
||||
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Error logging function
|
||||
log_error() {
|
||||
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1" | tee -a "$ERROR_LOG_FILE"
|
||||
}
|
||||
|
||||
# Function: Display usage
|
||||
display_usage() {
|
||||
echo "Usage: $0 [options]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " sessions - Show all complete backup sessions"
|
||||
echo " incomplete - Show incomplete backup sessions"
|
||||
echo " all - Show all snapshots grouped by session"
|
||||
echo " validate <tag> - Validate a specific backup session"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 sessions"
|
||||
echo " $0 validate manual-backup-2024-01-15_10-30-45"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Function: Validate repository access
|
||||
validate_repository() {
|
||||
log_message "Validating repository access..."
|
||||
|
||||
if [ ! -d "$backupPath" ]; then
|
||||
log_error "Backup path '$backupPath' does not exist or is not accessible."
|
||||
echo "Restic repository path not found. Please verify that the path exists and is mounted."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! RESTIC_PASSWORD=$(cat "$password_file") restic -r "$backupPath" snapshots &>/dev/null; then
|
||||
log_message "Attempting to fix permissions for '$backupPath'..."
|
||||
sudo chown -R "$(whoami)":"$(whoami)" "$backupPath" 2>/dev/null || true
|
||||
sudo chmod -R u+rwX "$backupPath" 2>/dev/null || true
|
||||
|
||||
if ! RESTIC_PASSWORD=$(cat "$password_file") restic -r "$backupPath" snapshots &>/dev/null; then
|
||||
log_error "Unable to access Restic repository at '$backupPath'."
|
||||
echo "Restic repository is inaccessible. Please check the path, permissions, or mount status."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log_message "Repository access validated successfully."
|
||||
}
|
||||
|
||||
# Function: Get all snapshots with session analysis
|
||||
get_all_snapshots() {
|
||||
log_message "Retrieving all snapshots for session analysis..."
|
||||
|
||||
local snapshots_json
|
||||
snapshots_json=$(RESTIC_PASSWORD=$(cat "$password_file") restic -r "$backupPath" snapshots --json 2>/dev/null)
|
||||
|
||||
if [ $? -ne 0 ] || [ -z "$snapshots_json" ]; then
|
||||
log_error "Failed to retrieve snapshots from repository."
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$snapshots_json"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function: Extract backup session tags
|
||||
extract_session_tags() {
|
||||
local snapshots_json="$1"
|
||||
|
||||
# Extract all session tags (manual-backup-* and automated-backup-*)
|
||||
echo "$snapshots_json" | jq -r '.[].tags[]' | grep -E '^(manual|automated)-backup-[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}$' | sort | uniq
|
||||
}
|
||||
|
||||
# Function: Analyze backup session completeness
|
||||
analyze_session_completeness() {
|
||||
local snapshots_json="$1"
|
||||
local session_tag="$2"
|
||||
|
||||
# Get all snapshots for this session
|
||||
local session_snapshots
|
||||
session_snapshots=$(echo "$snapshots_json" | jq -r ".[] | select(.tags[] | contains(\"$session_tag\"))")
|
||||
|
||||
# Count snapshots in session
|
||||
local snapshot_count
|
||||
snapshot_count=$(echo "$session_snapshots" | jq -s '. | length')
|
||||
|
||||
# Extract component tags
|
||||
local has_core=false
|
||||
local has_media=false
|
||||
local has_db=false
|
||||
|
||||
if echo "$session_snapshots" | jq -r '.tags[]' | grep -q "^core_files$"; then
|
||||
has_core=true
|
||||
fi
|
||||
|
||||
if echo "$session_snapshots" | jq -r '.tags[]' | grep -q "^media_themes$"; then
|
||||
has_media=true
|
||||
fi
|
||||
|
||||
if echo "$session_snapshots" | jq -r '.tags[]' | grep -q "^wordpress_db$"; then
|
||||
has_db=true
|
||||
fi
|
||||
|
||||
# Determine session status
|
||||
local status="INCOMPLETE"
|
||||
local missing_components=()
|
||||
|
||||
if [ "$has_core" = true ] && [ "$has_media" = true ] && [ "$has_db" = true ] && [ "$snapshot_count" -eq 3 ]; then
|
||||
status="COMPLETE"
|
||||
else
|
||||
if [ "$has_core" = false ]; then
|
||||
missing_components+=("core_files")
|
||||
fi
|
||||
if [ "$has_media" = false ]; then
|
||||
missing_components+=("media_themes")
|
||||
fi
|
||||
if [ "$has_db" = false ]; then
|
||||
missing_components+=("wordpress_db")
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get session timestamp
|
||||
local session_time
|
||||
session_time=$(echo "$session_snapshots" | jq -r '.time' | head -n1)
|
||||
|
||||
# Output session info
|
||||
echo "$session_tag|$status|$snapshot_count|$session_time|$(IFS=,; echo "${missing_components[*]}")"
|
||||
}
|
||||
|
||||
# Function: Display complete backup sessions
|
||||
display_complete_sessions() {
|
||||
log_message "Displaying complete backup sessions..."
|
||||
|
||||
local snapshots_json
|
||||
snapshots_json=$(get_all_snapshots)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to retrieve snapshots."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local session_tags
|
||||
session_tags=$(extract_session_tags "$snapshots_json")
|
||||
|
||||
if [ -z "$session_tags" ]; then
|
||||
echo "No backup sessions found."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "=== COMPLETE BACKUP SESSIONS ==="
|
||||
echo "Format: [Session Tag] [Date/Time] [Status]"
|
||||
echo "========================================"
|
||||
|
||||
local complete_sessions=0
|
||||
|
||||
while IFS= read -r session_tag; do
|
||||
if [ -n "$session_tag" ]; then
|
||||
local session_info
|
||||
session_info=$(analyze_session_completeness "$snapshots_json" "$session_tag")
|
||||
|
||||
local status
|
||||
status=$(echo "$session_info" | cut -d'|' -f2)
|
||||
|
||||
if [ "$status" = "COMPLETE" ]; then
|
||||
local session_time
|
||||
session_time=$(echo "$session_info" | cut -d'|' -f4)
|
||||
|
||||
printf "%-45s %s [%s]\n" "$session_tag" "$session_time" "$status"
|
||||
((complete_sessions++))
|
||||
fi
|
||||
fi
|
||||
done <<< "$session_tags"
|
||||
|
||||
echo "========================================"
|
||||
echo "Total complete backup sessions: $complete_sessions"
|
||||
echo ""
|
||||
echo "To restore a complete session, use:"
|
||||
echo " ./restore_full_backup_session.sh <session_tag>"
|
||||
}
|
||||
|
||||
# Function: Display incomplete backup sessions
|
||||
display_incomplete_sessions() {
|
||||
log_message "Displaying incomplete backup sessions..."
|
||||
|
||||
local snapshots_json
|
||||
snapshots_json=$(get_all_snapshots)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to retrieve snapshots."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local session_tags
|
||||
session_tags=$(extract_session_tags "$snapshots_json")
|
||||
|
||||
if [ -z "$session_tags" ]; then
|
||||
echo "No backup sessions found."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "=== INCOMPLETE BACKUP SESSIONS ==="
|
||||
echo "Format: [Session Tag] [Date/Time] [Status] [Missing Components]"
|
||||
echo "================================================================="
|
||||
|
||||
local incomplete_sessions=0
|
||||
|
||||
while IFS= read -r session_tag; do
|
||||
if [ -n "$session_tag" ]; then
|
||||
local session_info
|
||||
session_info=$(analyze_session_completeness "$snapshots_json" "$session_tag")
|
||||
|
||||
local status
|
||||
status=$(echo "$session_info" | cut -d'|' -f2)
|
||||
|
||||
if [ "$status" = "INCOMPLETE" ]; then
|
||||
local session_time missing_components
|
||||
session_time=$(echo "$session_info" | cut -d'|' -f4)
|
||||
missing_components=$(echo "$session_info" | cut -d'|' -f5)
|
||||
|
||||
printf "%-45s %s [%s] Missing: %s\n" "$session_tag" "$session_time" "$status" "$missing_components"
|
||||
((incomplete_sessions++))
|
||||
fi
|
||||
fi
|
||||
done <<< "$session_tags"
|
||||
|
||||
echo "================================================================="
|
||||
echo "Total incomplete backup sessions: $incomplete_sessions"
|
||||
echo ""
|
||||
echo "WARNING: Incomplete sessions cannot be used for full restoration."
|
||||
}
|
||||
|
||||
# Function: Display all sessions with details
|
||||
display_all_sessions() {
|
||||
log_message "Displaying all backup sessions with details..."
|
||||
|
||||
local snapshots_json
|
||||
snapshots_json=$(get_all_snapshots)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to retrieve snapshots."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local session_tags
|
||||
session_tags=$(extract_session_tags "$snapshots_json")
|
||||
|
||||
if [ -z "$session_tags" ]; then
|
||||
echo "No backup sessions found."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "=== ALL BACKUP SESSIONS ==="
|
||||
echo "Format: [Session Tag] [Date/Time] [Status] [Snapshot Count] [Missing Components]"
|
||||
echo "============================================================================="
|
||||
|
||||
local total_sessions=0
|
||||
local complete_sessions=0
|
||||
|
||||
while IFS= read -r session_tag; do
|
||||
if [ -n "$session_tag" ]; then
|
||||
local session_info
|
||||
session_info=$(analyze_session_completeness "$snapshots_json" "$session_tag")
|
||||
|
||||
local status session_time snapshot_count missing_components
|
||||
status=$(echo "$session_info" | cut -d'|' -f2)
|
||||
snapshot_count=$(echo "$session_info" | cut -d'|' -f3)
|
||||
session_time=$(echo "$session_info" | cut -d'|' -f4)
|
||||
missing_components=$(echo "$session_info" | cut -d'|' -f5)
|
||||
|
||||
if [ "$status" = "COMPLETE" ]; then
|
||||
printf "%-45s %s [%s] (%d snapshots)\n" "$session_tag" "$session_time" "$status" "$snapshot_count"
|
||||
((complete_sessions++))
|
||||
else
|
||||
printf "%-45s %s [%s] (%d snapshots) Missing: %s\n" "$session_tag" "$session_time" "$status" "$snapshot_count" "$missing_components"
|
||||
fi
|
||||
|
||||
((total_sessions++))
|
||||
fi
|
||||
done <<< "$session_tags"
|
||||
|
||||
echo "============================================================================="
|
||||
echo "Total sessions: $total_sessions (Complete: $complete_sessions, Incomplete: $((total_sessions - complete_sessions)))"
|
||||
}
|
||||
|
||||
# Function: Validate specific backup session
|
||||
validate_backup_session() {
|
||||
local session_tag="$1"
|
||||
|
||||
log_message "Validating backup session: $session_tag"
|
||||
|
||||
# Validate session tag format
|
||||
if [[ ! "$session_tag" =~ ^(manual|automated)-backup-[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}$ ]]; then
|
||||
echo "ERROR: Invalid backup session tag format."
|
||||
echo "Expected format: manual-backup-YYYY-MM-DD_HH-MM-SS or automated-backup-YYYY-MM-DD_HH-MM-SS"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local snapshots_json
|
||||
snapshots_json=$(get_all_snapshots)
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to retrieve snapshots."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if session exists
|
||||
local session_snapshots
|
||||
session_snapshots=$(echo "$snapshots_json" | jq -r ".[] | select(.tags[] | contains(\"$session_tag\"))")
|
||||
|
||||
if [ -z "$session_snapshots" ]; then
|
||||
echo "ERROR: No snapshots found for session tag '$session_tag'."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Analyze session
|
||||
local session_info
|
||||
session_info=$(analyze_session_completeness "$snapshots_json" "$session_tag")
|
||||
|
||||
local status snapshot_count session_time missing_components
|
||||
status=$(echo "$session_info" | cut -d'|' -f2)
|
||||
snapshot_count=$(echo "$session_info" | cut -d'|' -f3)
|
||||
session_time=$(echo "$session_info" | cut -d'|' -f4)
|
||||
missing_components=$(echo "$session_info" | cut -d'|' -f5)
|
||||
|
||||
echo "=== BACKUP SESSION VALIDATION ==="
|
||||
echo "Session Tag: $session_tag"
|
||||
echo "Session Time: $session_time"
|
||||
echo "Status: $status"
|
||||
echo "Snapshot Count: $snapshot_count"
|
||||
|
||||
if [ "$status" = "COMPLETE" ]; then
|
||||
echo "✓ All required components present:"
|
||||
echo " ✓ Core Files (core_files)"
|
||||
echo " ✓ Media Files (media_themes)"
|
||||
echo " ✓ Database (wordpress_db)"
|
||||
echo ""
|
||||
echo "This session is ready for full restoration using:"
|
||||
echo " ./restore_full_backup_session.sh $session_tag"
|
||||
else
|
||||
echo "✗ Session is incomplete. Missing components:"
|
||||
IFS=',' read -ra MISSING <<< "$missing_components"
|
||||
for component in "${MISSING[@]}"; do
|
||||
echo " ✗ $component"
|
||||
done
|
||||
echo ""
|
||||
echo "WARNING: This session cannot be used for full restoration."
|
||||
fi
|
||||
|
||||
echo "================================="
|
||||
|
||||
# Show individual snapshots in session
|
||||
echo ""
|
||||
echo "Individual snapshots in this session:"
|
||||
echo "$session_snapshots" | jq -r '" " + .short_id + " - " + .time + " - Tags: " + (.tags | join(", "))'
|
||||
}
|
||||
|
||||
# Function: Validate environment
|
||||
validate_environment() {
|
||||
if [ ! -f "$password_file" ]; then
|
||||
log_error "Password file not found at $password_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
validate_repository
|
||||
}
|
||||
|
||||
# Main script logic
|
||||
main() {
|
||||
local action="${1:-sessions}"
|
||||
|
||||
case "$action" in
|
||||
"sessions")
|
||||
validate_environment
|
||||
display_complete_sessions
|
||||
;;
|
||||
"incomplete")
|
||||
validate_environment
|
||||
display_incomplete_sessions
|
||||
;;
|
||||
"all")
|
||||
validate_environment
|
||||
display_all_sessions
|
||||
;;
|
||||
"validate")
|
||||
if [ -z "$2" ]; then
|
||||
echo "ERROR: Session tag required for validation."
|
||||
echo "Usage: $0 validate <session_tag>"
|
||||
exit 1
|
||||
fi
|
||||
validate_environment
|
||||
validate_backup_session "$2"
|
||||
;;
|
||||
*)
|
||||
display_usage
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
Loading…
Reference in New Issue