cleanup: Archive old complex scripts and documentation

- Move all old complex backup scripts to old_scripts/
- Archive previous documentation versions
- Clean up temporary files and debian packages
- Update README to focus on new simple system
- Keep only the enhanced simple backup system in main directory

Main directory now contains only:
- simple_backup_gui.py (GUI interface)
- enhanced_simple_backup.sh (CLI interface)
- list_drives.sh (helper)
- simple_backup.sh (basic CLI)
- SIMPLE_BACKUP_README.md (detailed docs)
- README.md (project overview)
This commit is contained in:
root
2025-10-09 00:30:03 +02:00
parent 871a57947d
commit 72f9838f55
36 changed files with 4657 additions and 2661 deletions

View File

@@ -0,0 +1,23 @@
#!/bin/bash
# LVM Backup Manager GUI Wrapper
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "LVM Backup Manager requires root privileges."
echo "Please run with: sudo lvm-backup-manager"
# Try to launch with pkexec if available
if command -v pkexec >/dev/null 2>&1; then
exec pkexec "$0" "$@"
else
exit 1
fi
fi
# Set proper path
SCRIPT_DIR="$(dirname "$0")"
export PATH="$SCRIPT_DIR:$PATH"
# Launch the GUI
cd "$SCRIPT_DIR"
exec python3 "$SCRIPT_DIR/lvm_backup_gui.py" "$@"

View File

@@ -0,0 +1,193 @@
#!/bin/bash
# LVM Block-Level Backup Script
# Creates consistent snapshots and clones entire volumes block-for-block
# This is the ONLY backup script you need!
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Configuration
SOURCE_VG="internal-vg"
TARGET_VG="migration-vg"
SNAPSHOT_SIZE="2G"
LOG_FILE="/var/log/lvm-block-backup.log"
log() {
local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
echo -e "${BLUE}$message${NC}"
echo "$message" >> "$LOG_FILE"
}
error() {
local message="[ERROR] $1"
echo -e "${RED}$message${NC}" >&2
echo "$message" >> "$LOG_FILE"
exit 1
}
success() {
local message="[SUCCESS] $1"
echo -e "${GREEN}$message${NC}"
echo "$message" >> "$LOG_FILE"
}
warning() {
local message="[WARNING] $1"
echo -e "${YELLOW}$message${NC}"
echo "$message" >> "$LOG_FILE"
}
check_requirements() {
log "Checking system requirements..."
# Check if running as root
if [ "$EUID" -ne 0 ]; then
error "This script must be run as root. Use: sudo $0"
fi
# Check if source VG exists
if ! vgs "$SOURCE_VG" >/dev/null 2>&1; then
error "Source volume group '$SOURCE_VG' not found"
fi
# Check if target VG exists
if ! vgs "$TARGET_VG" >/dev/null 2>&1; then
error "Target volume group '$TARGET_VG' not found"
fi
# Check if volumes exist
local volumes=("root" "home" "boot")
for vol in "${volumes[@]}"; do
if ! lvs "$SOURCE_VG/$vol" >/dev/null 2>&1; then
error "Source logical volume '$SOURCE_VG/$vol' not found"
fi
if ! lvs "$TARGET_VG/$vol" >/dev/null 2>&1; then
error "Target logical volume '$TARGET_VG/$vol' not found"
fi
done
# Check available space for snapshots
local vg_free=$(vgs --noheadings -o vg_free --units g "$SOURCE_VG" | tr -d ' G')
local vg_free_int=${vg_free%.*}
if [ "$vg_free_int" -lt 6 ]; then
error "Insufficient free space for snapshots. Need at least 6GB, have ${vg_free}GB"
fi
success "System requirements check passed"
}
cleanup_snapshots() {
log "Cleaning up any existing snapshots..."
lvremove -f "$SOURCE_VG/root-backup-snap" 2>/dev/null || true
lvremove -f "$SOURCE_VG/home-backup-snap" 2>/dev/null || true
lvremove -f "$SOURCE_VG/boot-backup-snap" 2>/dev/null || true
}
create_snapshots() {
log "Creating LVM snapshots for consistent backup..."
cleanup_snapshots
# Create snapshots
lvcreate -L "$SNAPSHOT_SIZE" -s -n root-backup-snap "$SOURCE_VG/root" || error "Failed to create root snapshot"
lvcreate -L "$SNAPSHOT_SIZE" -s -n home-backup-snap "$SOURCE_VG/home" || error "Failed to create home snapshot"
lvcreate -L 1G -s -n boot-backup-snap "$SOURCE_VG/boot" || error "Failed to create boot snapshot"
success "Snapshots created successfully"
}
clone_volumes() {
log "Starting block-level volume cloning..."
# Unmount target volumes if mounted
umount "/dev/$TARGET_VG/home" 2>/dev/null || true
umount "/dev/$TARGET_VG/root" 2>/dev/null || true
umount "/dev/$TARGET_VG/boot" 2>/dev/null || true
# Clone root volume
log "Cloning root volume (this may take a while)..."
dd if="/dev/$SOURCE_VG/root-backup-snap" of="/dev/$TARGET_VG/root" bs=64M status=progress || error "Failed to clone root volume"
success "Root volume cloned"
# Clone home volume
log "Cloning home volume (this will take the longest)..."
dd if="/dev/$SOURCE_VG/home-backup-snap" of="/dev/$TARGET_VG/home" bs=64M status=progress || error "Failed to clone home volume"
success "Home volume cloned"
# Clone boot volume
log "Cloning boot volume..."
dd if="/dev/$SOURCE_VG/boot-backup-snap" of="/dev/$TARGET_VG/boot" bs=64M status=progress || error "Failed to clone boot volume"
success "Boot volume cloned"
}
verify_backup() {
log "Verifying backup integrity..."
# Check filesystem integrity
fsck -n "/dev/$TARGET_VG/root" || warning "Root filesystem check showed issues"
fsck -n "/dev/$TARGET_VG/boot" || warning "Boot filesystem check showed issues"
# Note: Can't check encrypted home volume without decryption
success "Backup verification completed"
}
show_backup_info() {
log "Creating backup information..."
cat << EOF
=====================================
LVM Block-Level Backup Complete
=====================================
Date: $(date)
Source: $SOURCE_VG
Target: $TARGET_VG
Volume Information:
$(lvs $SOURCE_VG $TARGET_VG)
The external drive now contains an exact block-level copy of your internal drive.
You can boot from the external drive by selecting it in your BIOS/UEFI boot menu.
To mount the backup volumes:
sudo mount /dev/$TARGET_VG/root /mnt/backup-root
sudo mount /dev/$TARGET_VG/boot /mnt/backup-boot
# Home volume needs LUKS decryption first
EOF
}
main() {
echo -e "${GREEN}=== LVM Block-Level Backup Tool ===${NC}"
echo "This will create an exact copy of your internal LVM volumes to the external drive."
echo
read -p "Are you sure you want to proceed? This will OVERWRITE data on $TARGET_VG! [y/N]: " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
echo "Backup cancelled."
exit 0
fi
check_requirements
create_snapshots
clone_volumes
cleanup_snapshots
verify_backup
show_backup_info
success "Backup completed successfully!"
}
# Trap to cleanup on exit
trap cleanup_snapshots EXIT
main "$@"

View File

@@ -0,0 +1,541 @@
#!/usr/bin/env python3
"""
LVM Backup GUI - Professional interface for LVM snapshot backups
Creates block-level clones of LVM volumes with progress monitoring
"""
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import subprocess
import threading
import re
import os
import time
from datetime import datetime, timedelta
class LVMBackupGUI:
def __init__(self, root):
self.root = root
self.root.title("LVM Backup Manager")
self.root.geometry("900x700")
self.root.resizable(True, True)
# Configure style
self.setup_styles()
# Variables
self.source_vg = tk.StringVar()
self.target_vg = tk.StringVar()
self.backup_running = False
self.backup_process = None
# Create GUI
self.create_widgets()
self.refresh_drives()
def setup_styles(self):
"""Configure modern styling"""
style = ttk.Style()
# Configure colors and fonts
self.colors = {
'primary': '#2196F3',
'secondary': '#FFC107',
'success': '#4CAF50',
'danger': '#F44336',
'warning': '#FF9800',
'light': '#F5F5F5',
'dark': '#333333'
}
style.configure('Title.TLabel', font=('Arial', 16, 'bold'))
style.configure('Heading.TLabel', font=('Arial', 12, 'bold'))
style.configure('Info.TLabel', font=('Arial', 10))
def create_widgets(self):
"""Create the main GUI interface"""
# Main container with padding
main_frame = ttk.Frame(self.root, padding="20")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# Configure grid weights
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
main_frame.columnconfigure(1, weight=1)
# Title
title_label = ttk.Label(main_frame, text="🛡️ LVM Backup Manager", style='Title.TLabel')
title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))
# Source drive selection
self.create_drive_selection_frame(main_frame, "Source Drive", 1, self.source_vg, True)
# Arrow
arrow_label = ttk.Label(main_frame, text="⬇️", font=('Arial', 20))
arrow_label.grid(row=2, column=1, pady=10)
# Target drive selection
self.create_drive_selection_frame(main_frame, "Target Drive", 3, self.target_vg, False)
# Backup info frame
self.create_backup_info_frame(main_frame, 4)
# Control buttons
self.create_control_frame(main_frame, 5)
# Progress frame
self.create_progress_frame(main_frame, 6)
# Log frame
self.create_log_frame(main_frame, 7)
def create_drive_selection_frame(self, parent, title, row, var, is_source):
"""Create drive selection section"""
frame = ttk.LabelFrame(parent, text=title, padding="10")
frame.grid(row=row, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
frame.columnconfigure(1, weight=1)
# Drive dropdown
ttk.Label(frame, text="Volume Group:").grid(row=0, column=0, sticky=tk.W, padx=(0, 10))
combo = ttk.Combobox(frame, textvariable=var, state='readonly', width=30)
combo.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(0, 10))
combo.bind('<<ComboboxSelected>>', lambda e: self.update_drive_info())
refresh_btn = ttk.Button(frame, text="🔄 Refresh", command=self.refresh_drives)
refresh_btn.grid(row=0, column=2)
# Drive info labels
info_frame = ttk.Frame(frame)
info_frame.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(10, 0))
info_frame.columnconfigure(1, weight=1)
# Store references for updating
setattr(self, f"{title.lower().replace(' ', '_')}_info", info_frame)
if is_source:
self.source_combo = combo
else:
self.target_combo = combo
def create_backup_info_frame(self, parent, row):
"""Create backup information display"""
frame = ttk.LabelFrame(parent, text="📊 Backup Information", padding="10")
frame.grid(row=row, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
frame.columnconfigure(1, weight=1)
self.backup_info_labels = {}
info_items = [
("Total Size:", "total_size"),
("Estimated Time:", "est_time"),
("Transfer Speed:", "speed"),
("Status:", "status")
]
for i, (label, key) in enumerate(info_items):
ttk.Label(frame, text=label).grid(row=i//2, column=(i%2)*2, sticky=tk.W, padx=(0, 10), pady=2)
value_label = ttk.Label(frame, text="Not calculated", style='Info.TLabel')
value_label.grid(row=i//2, column=(i%2)*2+1, sticky=tk.W, padx=(0, 20), pady=2)
self.backup_info_labels[key] = value_label
def create_control_frame(self, parent, row):
"""Create control buttons"""
frame = ttk.Frame(parent)
frame.grid(row=row, column=0, columnspan=3, pady=20)
self.start_btn = ttk.Button(frame, text="▶️ Start Backup", command=self.start_backup, style='Accent.TButton')
self.start_btn.pack(side=tk.LEFT, padx=(0, 10))
self.stop_btn = ttk.Button(frame, text="⏹️ Stop Backup", command=self.stop_backup, state='disabled')
self.stop_btn.pack(side=tk.LEFT, padx=(0, 10))
self.verify_btn = ttk.Button(frame, text="✅ Verify Backup", command=self.verify_backup)
self.verify_btn.pack(side=tk.LEFT)
def create_progress_frame(self, parent, row):
"""Create progress monitoring"""
frame = ttk.LabelFrame(parent, text="📈 Progress", padding="10")
frame.grid(row=row, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=5)
frame.columnconfigure(0, weight=1)
# Overall progress
ttk.Label(frame, text="Overall Progress:").grid(row=0, column=0, sticky=tk.W)
self.overall_progress = ttk.Progressbar(frame, mode='determinate', length=400)
self.overall_progress.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=(5, 10))
# Current operation
self.current_operation = ttk.Label(frame, text="Ready to start backup", style='Info.TLabel')
self.current_operation.grid(row=2, column=0, sticky=tk.W)
# Time remaining
self.time_remaining = ttk.Label(frame, text="", style='Info.TLabel')
self.time_remaining.grid(row=3, column=0, sticky=tk.W)
def create_log_frame(self, parent, row):
"""Create log output"""
frame = ttk.LabelFrame(parent, text="📝 Log Output", padding="10")
frame.grid(row=row, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
frame.columnconfigure(0, weight=1)
frame.rowconfigure(0, weight=1)
parent.rowconfigure(row, weight=1)
self.log_text = scrolledtext.ScrolledText(frame, height=12, width=80, font=('Consolas', 9))
self.log_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# Add initial message
self.log("LVM Backup Manager initialized")
self.log("Select source and target drives to begin")
def log(self, message):
"""Add message to log with timestamp"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_message = f"[{timestamp}] {message}\n"
self.log_text.insert(tk.END, log_message)
self.log_text.see(tk.END)
self.root.update_idletasks()
def refresh_drives(self):
"""Scan for available LVM volume groups"""
try:
self.log("Scanning for LVM volume groups...")
# Get volume groups
result = subprocess.run(['sudo', 'vgs', '--noheadings', '-o', 'vg_name,vg_size,vg_free'],
capture_output=True, text=True, check=True)
vgs = []
for line in result.stdout.strip().split('\n'):
if line.strip():
parts = line.strip().split()
if len(parts) >= 3:
vg_name = parts[0]
vg_size = parts[1]
vg_free = parts[2]
vgs.append(f"{vg_name} ({vg_size} total, {vg_free} free)")
# Update comboboxes
self.source_combo['values'] = vgs
self.target_combo['values'] = vgs
if vgs:
self.log(f"Found {len(vgs)} volume groups")
else:
self.log("No LVM volume groups found")
except subprocess.CalledProcessError as e:
self.log(f"Error scanning drives: {e}")
messagebox.showerror("Error", "Failed to scan for LVM volume groups. Make sure you have LVM installed and proper permissions.")
def update_drive_info(self):
"""Update drive information when selection changes"""
if not self.source_vg.get() or not self.target_vg.get():
return
try:
source_vg = self.source_vg.get().split()[0]
target_vg = self.target_vg.get().split()[0]
# Get detailed volume information
source_info = self.get_vg_details(source_vg)
target_info = self.get_vg_details(target_vg)
# Calculate backup information
self.calculate_backup_info(source_info, target_info)
except Exception as e:
self.log(f"Error updating drive info: {e}")
def get_vg_details(self, vg_name):
"""Get detailed information about a volume group"""
try:
# Get VG info
vg_result = subprocess.run(['sudo', 'vgs', vg_name, '--noheadings', '-o', 'vg_size,vg_free,vg_uuid'],
capture_output=True, text=True, check=True)
vg_parts = vg_result.stdout.strip().split()
# Get LV info
lv_result = subprocess.run(['sudo', 'lvs', vg_name, '--noheadings', '-o', 'lv_name,lv_size'],
capture_output=True, text=True, check=True)
volumes = []
total_lv_size = 0
for line in lv_result.stdout.strip().split('\n'):
if line.strip():
parts = line.strip().split()
if len(parts) >= 2:
lv_name = parts[0]
lv_size = parts[1]
volumes.append((lv_name, lv_size))
# Convert size to bytes for calculation
size_bytes = self.parse_size_to_bytes(lv_size)
total_lv_size += size_bytes
return {
'name': vg_name,
'total_size': vg_parts[0],
'free_size': vg_parts[1],
'uuid': vg_parts[2],
'volumes': volumes,
'total_lv_size_bytes': total_lv_size
}
except subprocess.CalledProcessError:
return None
def parse_size_to_bytes(self, size_str):
"""Parse LVM size string to bytes"""
size_str = size_str.strip()
multipliers = {'B': 1, 'K': 1024, 'M': 1024**2, 'G': 1024**3, 'T': 1024**4}
# Extract number and unit
if size_str[-1].upper() in multipliers:
number = float(size_str[:-1])
unit = size_str[-1].upper()
else:
number = float(size_str)
unit = 'B'
return int(number * multipliers.get(unit, 1))
def calculate_backup_info(self, source_info, target_info):
"""Calculate and display backup information"""
if not source_info or not target_info:
return
# Calculate total size to backup
total_bytes = source_info['total_lv_size_bytes']
total_gb = total_bytes / (1024**3)
# Estimate time (based on typical speeds: 200-400 MB/s)
avg_speed_mbs = 250 # MB/s
est_seconds = total_bytes / (avg_speed_mbs * 1024 * 1024)
est_time = str(timedelta(seconds=int(est_seconds)))
# Update labels
self.backup_info_labels['total_size'].config(text=f"{total_gb:.1f} GB")
self.backup_info_labels['est_time'].config(text=est_time)
self.backup_info_labels['speed'].config(text=f"~{avg_speed_mbs} MB/s")
self.backup_info_labels['status'].config(text="Ready")
self.log(f"Backup calculation: {total_gb:.1f} GB, estimated {est_time}")
def start_backup(self):
"""Start the backup process"""
if not self.source_vg.get() or not self.target_vg.get():
messagebox.showerror("Error", "Please select both source and target drives")
return
source_vg = self.source_vg.get().split()[0]
target_vg = self.target_vg.get().split()[0]
if source_vg == target_vg:
messagebox.showerror("Error", "Source and target cannot be the same drive")
return
# Confirm backup
if not messagebox.askyesno("Confirm Backup",
f"This will overwrite all data on {target_vg}.\n\nAre you sure you want to continue?"):
return
# Update UI state
self.backup_running = True
self.start_btn.config(state='disabled')
self.stop_btn.config(state='normal')
self.overall_progress.config(value=0)
self.backup_info_labels['status'].config(text="Running...")
# Start backup in thread
self.backup_thread = threading.Thread(target=self.run_backup, args=(source_vg, target_vg))
self.backup_thread.daemon = True
self.backup_thread.start()
def run_backup(self, source_vg, target_vg):
"""Run the actual backup process"""
try:
self.log(f"Starting backup: {source_vg}{target_vg}")
# Modify the backup script to work with our selected VGs
script_path = os.path.join(os.path.dirname(__file__), 'lvm_block_backup.sh')
# Create a temporary script with modified VG names
temp_script = self.create_temp_script(script_path, source_vg, target_vg)
# Run the backup script
self.backup_process = subprocess.Popen(
['sudo', temp_script],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1
)
# Monitor progress
self.monitor_backup_progress()
except Exception as e:
self.log(f"Backup failed: {e}")
self.backup_finished(False)
def create_temp_script(self, original_script, source_vg, target_vg):
"""Create a temporary script with modified VG names"""
temp_script = '/tmp/lvm_backup_gui_temp.sh'
with open(original_script, 'r') as f:
content = f.read()
# Replace VG names
content = content.replace('SOURCE_VG="internal-vg"', f'SOURCE_VG="{source_vg}"')
content = content.replace('TARGET_VG="migration-vg"', f'TARGET_VG="{target_vg}"')
# Make it auto-answer 'y' to confirmation
content = content.replace('read -p "Are you sure you want to proceed?', 'echo "Auto-confirmed by GUI"; confirm="y"; #read -p "Are you sure you want to proceed?')
with open(temp_script, 'w') as f:
f.write(content)
os.chmod(temp_script, 0o755)
return temp_script
def monitor_backup_progress(self):
"""Monitor backup progress and update UI"""
if not self.backup_running or not self.backup_process:
return
try:
# Read output
line = self.backup_process.stdout.readline()
if line:
line = line.strip()
self.log(line)
# Parse progress from dd output
if 'kopiert' in line or 'copied' in line:
self.parse_dd_progress(line)
elif 'SUCCESS' in line:
if 'Root volume cloned' in line:
self.overall_progress.config(value=33)
self.current_operation.config(text="Root volume completed ✅")
elif 'Home volume cloned' in line:
self.overall_progress.config(value=90)
self.current_operation.config(text="Home volume completed ✅")
elif 'Boot volume cloned' in line:
self.overall_progress.config(value=95)
self.current_operation.config(text="Boot volume completed ✅")
elif 'Cloning' in line:
if 'root' in line.lower():
self.current_operation.config(text="📁 Cloning root volume...")
elif 'home' in line.lower():
self.current_operation.config(text="🏠 Cloning home volume...")
elif 'boot' in line.lower():
self.current_operation.config(text="⚡ Cloning boot volume...")
# Check if process is still running
if self.backup_process.poll() is None:
# Schedule next check
self.root.after(100, self.monitor_backup_progress)
else:
# Process finished
success = self.backup_process.returncode == 0
self.backup_finished(success)
except Exception as e:
self.log(f"Error monitoring progress: {e}")
self.backup_finished(False)
def parse_dd_progress(self, line):
"""Parse dd progress output"""
try:
# Look for speed information
if 'MB/s' in line:
speed_match = re.search(r'(\d+(?:\.\d+)?)\s*MB/s', line)
if speed_match:
speed = speed_match.group(1)
self.backup_info_labels['speed'].config(text=f"{speed} MB/s")
except:
pass
def backup_finished(self, success):
"""Handle backup completion"""
self.backup_running = False
self.start_btn.config(state='normal')
self.stop_btn.config(state='disabled')
if success:
self.overall_progress.config(value=100)
self.current_operation.config(text="✅ Backup completed successfully!")
self.backup_info_labels['status'].config(text="Completed")
self.log("🎉 Backup completed successfully!")
messagebox.showinfo("Success", "Backup completed successfully!")
else:
self.current_operation.config(text="❌ Backup failed")
self.backup_info_labels['status'].config(text="Failed")
self.log("❌ Backup failed")
messagebox.showerror("Error", "Backup failed. Check the log for details.")
# Clean up
if hasattr(self, 'backup_process'):
self.backup_process = None
def stop_backup(self):
"""Stop the running backup"""
if self.backup_process:
self.log("Stopping backup...")
self.backup_process.terminate()
self.backup_finished(False)
def verify_backup(self):
"""Verify the backup integrity"""
if not self.target_vg.get():
messagebox.showerror("Error", "Please select a target drive to verify")
return
target_vg = self.target_vg.get().split()[0]
self.log(f"Verifying backup on {target_vg}...")
def verify_thread():
try:
# Run filesystem checks
result = subprocess.run(['sudo', 'fsck', '-n', f'/dev/{target_vg}/root'],
capture_output=True, text=True)
if result.returncode == 0:
self.log("✅ Root filesystem verification passed")
else:
self.log("⚠️ Root filesystem verification issues detected")
result = subprocess.run(['sudo', 'fsck', '-n', f'/dev/{target_vg}/boot'],
capture_output=True, text=True)
if result.returncode == 0:
self.log("✅ Boot filesystem verification passed")
else:
self.log("⚠️ Boot filesystem verification issues detected")
self.log("Verification completed")
messagebox.showinfo("Verification", "Backup verification completed. Check log for details.")
except Exception as e:
self.log(f"Verification error: {e}")
messagebox.showerror("Error", f"Verification failed: {e}")
thread = threading.Thread(target=verify_thread)
thread.daemon = True
thread.start()
def main():
"""Main entry point"""
# Check if running as root
if os.geteuid() != 0:
messagebox.showerror("Permission Error",
"This application requires root privileges.\n\n" +
"Please run with: sudo python3 lvm_backup_gui.py")
return
root = tk.Tk()
app = LVMBackupGUI(root)
root.mainloop()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,11 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=LVM Backup Manager
Comment=Professional LVM volume backup with GUI
Exec=pkexec lvm-backup-manager
Icon=lvm-backup-manager
Categories=System;Administration;
Keywords=backup;lvm;snapshot;clone;system;recovery;
StartupNotify=true
StartupWMClass=LVM Backup Manager

View File

@@ -0,0 +1,58 @@
# LVM Backup Manager
## Overview
LVM Backup Manager is a professional graphical interface for creating block-level backups of LVM (Logical Volume Manager) volumes. It provides an intuitive way to clone entire volume groups while maintaining data consistency through LVM snapshots.
## Features
* **Intuitive GUI**: Easy-to-use interface with drive selection and progress monitoring
* **Real-time Progress**: Live progress bars and speed indicators during backup
* **Safety Features**: Confirmation dialogs and verification tools
* **Professional Logging**: Detailed logs with timestamps for troubleshooting
* **Snapshot-based**: Uses LVM snapshots for consistent, live backups
* **Block-level Cloning**: Creates exact, bootable copies of your volumes
## System Requirements
* Linux system with LVM2 installed
* Python 3.x with Tkinter support
* Root/sudo privileges for LVM operations
* External storage device with LVM volume group
## Usage
### GUI Application
```bash
sudo lvm-backup-manager
```
### Command Line (legacy)
```bash
sudo lvm-block-backup
```
## How It Works
1. **Detection**: Scans for available LVM volume groups
2. **Selection**: Choose source and target volume groups
3. **Calculation**: Estimates backup time and data size
4. **Snapshots**: Creates consistent snapshots of source volumes
5. **Cloning**: Performs block-level copy using dd command
6. **Verification**: Checks filesystem integrity of backup
7. **Cleanup**: Removes temporary snapshots
## Safety Notes
* **Always verify** your target drive selection
* **Backup process will overwrite** all data on target volume group
* **Test your backups** using the verification feature
* **Keep multiple backup copies** for critical data
## Support
For issues or questions, check the log output in the GUI application or examine system logs.
## License
This software is provided as-is for backup and recovery purposes.

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="64" height="64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#2196F3;stop-opacity:1" />
<stop offset="100%" style="stop-color:#1976D2;stop-opacity:1" />
</linearGradient>
<linearGradient id="diskGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#FFC107;stop-opacity:1" />
<stop offset="100%" style="stop-color:#FF9800;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Background circle -->
<circle cx="32" cy="32" r="30" fill="url(#bgGradient)" stroke="#1565C0" stroke-width="2"/>
<!-- Hard drive icon -->
<rect x="12" y="20" width="20" height="14" rx="2" fill="url(#diskGradient)" stroke="#E65100" stroke-width="1"/>
<rect x="14" y="22" width="16" height="2" fill="#FFF3E0" opacity="0.8"/>
<rect x="14" y="25" width="16" height="2" fill="#FFF3E0" opacity="0.6"/>
<rect x="14" y="28" width="16" height="2" fill="#FFF3E0" opacity="0.4"/>
<circle cx="29" cy="30" r="1.5" fill="#E65100"/>
<!-- Arrow pointing right -->
<path d="M 35 27 L 42 27 L 38 23 M 42 27 L 38 31" stroke="#4CAF50" stroke-width="2" fill="none" stroke-linecap="round"/>
<!-- Second hard drive -->
<rect x="44" y="20" width="20" height="14" rx="2" fill="url(#diskGradient)" stroke="#E65100" stroke-width="1"/>
<rect x="46" y="22" width="16" height="2" fill="#FFF3E0" opacity="0.8"/>
<rect x="46" y="25" width="16" height="2" fill="#FFF3E0" opacity="0.6"/>
<rect x="46" y="28" width="16" height="2" fill="#FFF3E0" opacity="0.4"/>
<circle cx="61" cy="30" r="1.5" fill="#E65100"/>
<!-- Shield/protection symbol -->
<path d="M 32 40 L 28 44 L 32 48 L 36 44 Z" fill="#4CAF50" stroke="#2E7D32" stroke-width="1"/>
<path d="M 30 44 L 32 46 L 36 42" stroke="white" stroke-width="1.5" fill="none" stroke-linecap="round"/>
<!-- LVM text -->
<text x="32" y="58" text-anchor="middle" font-family="Arial" font-size="8" font-weight="bold" fill="white">LVM</text>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB