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