feat: complete LVM backup system with external M.2 boot support
MAJOR MILESTONE: Transform backup system into comprehensive LVM migration solution 🎯 LVM Migration & Boot System Complete: - Complete external M.2 LVM migration capability - One-button migration from non-LVM to LVM system - Automatic GRUB repair and boot configuration - External boot validation and recovery tools 🔧 New Migration Tools Added: - fix_grub_lvm_boot.sh: Complete GRUB repair for external LVM boot - automated_clonezilla_backup.sh: Automated backup with Clonezilla integration - validate_lvm_migration.sh: Comprehensive migration validation - troubleshoot_migration.sh: Advanced diagnostic and repair tools - emergency_install.sh: Package installation for live systems - bootstrap_usb_tools.sh: USB preparation with all dependencies 💾 Backup System Enhancements: - create_alpine_backup_usb.sh: Alpine Linux live system preparation - create_clonezilla_backup.sh: Professional backup solution integration - plug_and_play_backup.sh: Simple automated backup workflow - lvm_snapshot_backup.sh: LVM snapshot-based incremental backups - simple_auto_backup.sh: Streamlined backup automation 📋 Documentation & Guides: - LIVE_USB_MIGRATION_GUIDE.md: Complete migration walkthrough - DRIVE_SELECTION_REFERENCE.md: Safe drive selection procedures - Comprehensive troubleshooting and validation procedures - Step-by-step migration instructions with safety checks 🛡️ Safety & Validation Features: - Interactive drive selection with confirmation - Comprehensive pre-migration checks - Automatic backup validation - GRUB boot repair with fallback options - Hardware compatibility verification 🧪 Testing & Debugging: - Complete GRUB configuration analysis - LVM volume validation and repair - Boot sequence troubleshooting - Hardware connection diagnostics ✅ Production Ready Status: - All migration tools tested and validated - External M.2 boot functionality confirmed - GRUB configuration properly generates LVM entries - Kernel files correctly deployed to external boot partition - EFI bootloader properly configured as 'ubuntu-external' This completes the transformation from simple backup scripts to a comprehensive LVM migration and backup system capable of full system migration to external M.2 with proper boot configuration and recovery capabilities.
This commit is contained in:
@@ -253,8 +253,8 @@ class BackupManager:
|
||||
return
|
||||
|
||||
# Get drive paths
|
||||
source = self.source_var.get().split()[0]
|
||||
target = self.target_var.get().split()[0]
|
||||
source = self.source_drive.get().split()[0]
|
||||
target = self.target_drive.get().split()[0]
|
||||
|
||||
self.run_backup_script("analyze", source, target)
|
||||
|
||||
@@ -304,8 +304,8 @@ class BackupManager:
|
||||
return
|
||||
|
||||
# Get drive paths
|
||||
source = self.source_var.get().split()[0]
|
||||
target = self.target_var.get().split()[0]
|
||||
source = self.source_drive.get().split()[0]
|
||||
target = self.target_drive.get().split()[0]
|
||||
|
||||
# Confirm operation
|
||||
result = messagebox.askyesno(
|
||||
@@ -324,21 +324,21 @@ class BackupManager:
|
||||
"""Run the backup script with specified mode"""
|
||||
try:
|
||||
# Clear previous output
|
||||
self.output_text.delete(1.0, tk.END)
|
||||
self.log_text.delete(1.0, tk.END)
|
||||
|
||||
# Determine command arguments
|
||||
if mode == "analyze":
|
||||
cmd = ['sudo', './backup_script.sh', '--analyze', '--source', source, '--target', target]
|
||||
self.log_message("🔍 Analyzing changes between drives...")
|
||||
self.log("🔍 Analyzing changes between drives...")
|
||||
elif mode == "sync":
|
||||
cmd = ['sudo', './backup_script.sh', '--sync', '--source', source, '--target', target]
|
||||
self.log_message("⚡ Starting smart sync backup...")
|
||||
self.log("⚡ Starting smart sync backup...")
|
||||
elif mode == "backup":
|
||||
cmd = ['sudo', './backup_script.sh', '--source', source, '--target', target]
|
||||
self.log_message("🔄 Starting full backup...")
|
||||
self.log("🔄 Starting full backup...")
|
||||
elif mode == "restore":
|
||||
cmd = ['sudo', './backup_script.sh', '--restore', '--source', source, '--target', target]
|
||||
self.log_message("🔧 Starting restore operation...")
|
||||
self.log("🔧 Starting restore operation...")
|
||||
else:
|
||||
raise ValueError(f"Unknown mode: {mode}")
|
||||
|
||||
@@ -362,8 +362,8 @@ class BackupManager:
|
||||
break
|
||||
if output:
|
||||
# Update GUI in real-time
|
||||
self.output_text.insert(tk.END, output)
|
||||
self.output_text.see(tk.END)
|
||||
self.log_text.insert(tk.END, output)
|
||||
self.log_text.see(tk.END)
|
||||
self.root.update()
|
||||
|
||||
# Get final result
|
||||
@@ -371,24 +371,24 @@ class BackupManager:
|
||||
|
||||
if return_code == 0:
|
||||
if mode == "analyze":
|
||||
self.log_message("✅ Analysis completed successfully!")
|
||||
self.log("✅ Analysis completed successfully!")
|
||||
messagebox.showinfo("Analysis Complete", "Drive analysis completed. Check the output for recommendations.")
|
||||
elif mode == "sync":
|
||||
self.log_message("✅ Smart sync completed successfully!")
|
||||
self.log("✅ Smart sync completed successfully!")
|
||||
messagebox.showinfo("Success", "Smart sync backup completed successfully!")
|
||||
elif mode == "backup":
|
||||
self.log_message("✅ Backup completed successfully!")
|
||||
self.log("✅ Backup completed successfully!")
|
||||
messagebox.showinfo("Success", "Full backup completed successfully!")
|
||||
elif mode == "restore":
|
||||
self.log_message("✅ Restore completed successfully!")
|
||||
self.log("✅ Restore completed successfully!")
|
||||
messagebox.showinfo("Success", "System restore completed successfully!")
|
||||
else:
|
||||
self.log_message(f"❌ {mode.title()} operation failed!")
|
||||
self.log(f"❌ {mode.title()} operation failed!")
|
||||
messagebox.showerror("Error", f"{mode.title()} operation failed. Check the output for details.")
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error running {mode} operation: {str(e)}"
|
||||
self.log_message(f"❌ {error_msg}")
|
||||
self.log(f"❌ {error_msg}")
|
||||
messagebox.showerror("Error", error_msg)
|
||||
|
||||
def check_existing_backup(self, target_drive):
|
||||
@@ -739,8 +739,8 @@ class BackupManager:
|
||||
if not result2:
|
||||
return
|
||||
|
||||
source = self.source_var.get().split()[0]
|
||||
target = self.target_var.get().split()[0]
|
||||
source = self.source_drive.get().split()[0]
|
||||
target = self.target_drive.get().split()[0]
|
||||
self.run_backup_script("restore", source, target)
|
||||
|
||||
def reboot_and_restore(self):
|
||||
@@ -788,8 +788,8 @@ class BackupManager:
|
||||
return
|
||||
|
||||
# Confirm backup
|
||||
source = self.source_var.get().split()[0]
|
||||
target = self.target_var.get().split()[0]
|
||||
source = self.source_drive.get().split()[0]
|
||||
target = self.target_drive.get().split()[0]
|
||||
|
||||
result = messagebox.askyesno("Confirm Backup",
|
||||
f"This will clone {source} to {target}.\n\n"
|
||||
@@ -896,38 +896,131 @@ class BackupManager:
|
||||
self.log("Stopping operation...")
|
||||
|
||||
def reboot_and_backup(self):
|
||||
"""Schedule reboot and backup"""
|
||||
"""Schedule reboot and backup - creates systemd service for after reboot"""
|
||||
if not self.validate_selection():
|
||||
return
|
||||
|
||||
source = self.source_drive.get().split()[0]
|
||||
target = self.target_drive.get().split()[0]
|
||||
|
||||
result = messagebox.askyesno("Confirm Reboot & Backup",
|
||||
"This will:\n"
|
||||
"1. Save current session\n"
|
||||
"2. Reboot the system\n"
|
||||
"3. Start backup after reboot\n\n"
|
||||
"Continue?")
|
||||
f"This will:\n"
|
||||
f"1. Create a one-time backup service\n"
|
||||
f"2. Reboot the system\n"
|
||||
f"3. Run backup before GUI starts (minimal system load)\n"
|
||||
f"4. System will be available after backup completes\n\n"
|
||||
f"Source: {source}\n"
|
||||
f"Target: {target}\n\n"
|
||||
f"⚠️ Target drive will be completely overwritten!\n\n"
|
||||
f"Continue?")
|
||||
|
||||
if not result:
|
||||
return
|
||||
|
||||
try:
|
||||
# Create backup script for after reboot
|
||||
script_content = self.create_reboot_operation_script("backup")
|
||||
# Create backup script directory
|
||||
backup_dir = "/opt/backup-system"
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Save script
|
||||
script_path = "/tmp/backup_after_reboot.sh"
|
||||
with open(script_path, 'w') as f:
|
||||
f.write(script_content)
|
||||
# Create the service directory
|
||||
subprocess.run(['sudo', 'mkdir', '-p', backup_dir], check=True)
|
||||
|
||||
os.chmod(script_path, 0o755)
|
||||
# Copy backup script to system location
|
||||
subprocess.run(['sudo', 'cp', f'{script_dir}/backup_script.sh', backup_dir], check=True)
|
||||
subprocess.run(['sudo', 'chmod', '+x', f'{backup_dir}/backup_script.sh'], check=True)
|
||||
|
||||
self.log("Reboot backup script created")
|
||||
self.log("System will reboot in 5 seconds...")
|
||||
# Create the backup service script
|
||||
service_script = f'''#!/bin/bash
|
||||
# One-time backup service script
|
||||
set -e
|
||||
|
||||
# Wait for system to stabilize
|
||||
sleep 10
|
||||
|
||||
# Log everything
|
||||
exec > /var/log/reboot-backup.log 2>&1
|
||||
|
||||
echo "Starting reboot backup at $(date)"
|
||||
echo "Source: {source}"
|
||||
echo "Target: {target}"
|
||||
|
||||
# Run the backup
|
||||
cd {backup_dir}
|
||||
./backup_script.sh --source {source} --target {target}
|
||||
|
||||
# Disable this service after completion
|
||||
systemctl disable reboot-backup.service
|
||||
rm -f /etc/systemd/system/reboot-backup.service
|
||||
rm -rf {backup_dir}
|
||||
|
||||
echo "Backup completed at $(date)"
|
||||
echo "System ready for normal use"
|
||||
|
||||
# Notify desktop (if user session exists)
|
||||
if [ -n "$DISPLAY" ]; then
|
||||
notify-send "Backup Complete" "System backup finished successfully" || true
|
||||
fi
|
||||
'''
|
||||
|
||||
# Schedule reboot
|
||||
subprocess.run(['sudo', 'shutdown', '-r', '+1'], check=True)
|
||||
# Write service script
|
||||
with open('/tmp/reboot-backup.sh', 'w') as f:
|
||||
f.write(service_script)
|
||||
|
||||
self.log("Reboot scheduled. Backup will start automatically after reboot.")
|
||||
subprocess.run(['sudo', 'mv', '/tmp/reboot-backup.sh', f'{backup_dir}/reboot-backup.sh'], check=True)
|
||||
subprocess.run(['sudo', 'chmod', '+x', f'{backup_dir}/reboot-backup.sh'], check=True)
|
||||
|
||||
# Create systemd service
|
||||
service_content = f'''[Unit]
|
||||
Description=One-time System Backup After Reboot
|
||||
After=multi-user.target
|
||||
Wants=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart={backup_dir}/reboot-backup.sh
|
||||
User=root
|
||||
StandardOutput=file:/var/log/reboot-backup.log
|
||||
StandardError=file:/var/log/reboot-backup.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
'''
|
||||
|
||||
# Write service file
|
||||
with open('/tmp/reboot-backup.service', 'w') as f:
|
||||
f.write(service_content)
|
||||
|
||||
subprocess.run(['sudo', 'mv', '/tmp/reboot-backup.service', '/etc/systemd/system/'], check=True)
|
||||
|
||||
# Enable the service for next boot only
|
||||
subprocess.run(['sudo', 'systemctl', 'daemon-reload'], check=True)
|
||||
subprocess.run(['sudo', 'systemctl', 'enable', 'reboot-backup.service'], check=True)
|
||||
|
||||
self.log("✅ Backup service created and enabled")
|
||||
self.log("📝 Backup will run automatically after reboot")
|
||||
self.log("📋 Check /var/log/reboot-backup.log for progress")
|
||||
|
||||
# Final confirmation
|
||||
final_result = messagebox.askyesno(
|
||||
"Ready to Reboot",
|
||||
"Backup service is configured!\n\n"
|
||||
"After reboot:\n"
|
||||
"• Backup will start automatically\n"
|
||||
"• Progress logged to /var/log/reboot-backup.log\n"
|
||||
"• System will be available after completion\n"
|
||||
"• Service will self-destruct when done\n\n"
|
||||
"Reboot now?"
|
||||
)
|
||||
|
||||
if final_result:
|
||||
self.log("🔄 Rebooting system...")
|
||||
subprocess.run(['sudo', 'systemctl', 'reboot'], check=True)
|
||||
else:
|
||||
self.log("⏸️ Reboot cancelled - service is ready when you reboot manually")
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"❌ Error setting up reboot backup: {e}")
|
||||
messagebox.showerror("Error", f"Failed to setup reboot backup: {e}")
|
||||
|
||||
except Exception as e:
|
||||
self.log(f"Error scheduling reboot: {e}")
|
||||
|
||||
Reference in New Issue
Block a user