diff --git a/migrate_lvm_to_internal.sh b/migrate_lvm_to_internal.sh new file mode 100755 index 0000000..d361f92 --- /dev/null +++ b/migrate_lvm_to_internal.sh @@ -0,0 +1,407 @@ +#!/bin/bash + +# LVM Migration Script: External M.2 to Internal NVMe +# This script migrates the complete LVM structure from external M.2 to internal NVMe +# with snapshot capabilities for future backups + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration +EXTERNAL_DRIVE="/dev/sda" +INTERNAL_DRIVE="/dev/nvme0n1" +VG_NAME="migration-vg" +NEW_VG_NAME="internal-vg" # New VG name for internal drive +BACKUP_DIR="/tmp/lvm_migration_backup" + +# Logging +LOG_FILE="/var/log/lvm_migration.log" +exec 1> >(tee -a "$LOG_FILE") +exec 2> >(tee -a "$LOG_FILE" >&2) + +log_info() { + echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1" +} + +log_step() { + echo -e "${BLUE}[STEP]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1" +} + +# Safety checks +safety_checks() { + log_step "Performing safety checks..." + + # Check if running as root + if [[ $EUID -ne 0 ]]; then + log_error "This script must be run as root (use sudo)" + exit 1 + fi + + # Verify we're booted from external drive + root_device=$(findmnt -n -o SOURCE /) + if [[ "$root_device" != "/dev/mapper/migration--vg-root" ]]; then + log_error "Not booted from external LVM! Current root: $root_device" + log_error "Please boot from external M.2 drive first" + exit 1 + fi + + # Check drives exist + if [[ ! -b "$EXTERNAL_DRIVE" ]]; then + log_error "External drive $EXTERNAL_DRIVE not found" + exit 1 + fi + + if [[ ! -b "$INTERNAL_DRIVE" ]]; then + log_error "Internal drive $INTERNAL_DRIVE not found" + exit 1 + fi + + # Check available space + vg_size=$(vgs --noheadings --units g --nosuffix -o vg_size "$VG_NAME" | tr -d ' ') + internal_size=$(lsblk -b -n -o SIZE "$INTERNAL_DRIVE" | awk '{printf "%.0f", $1/1024/1024/1024}') + + if (( $(echo "$vg_size > $internal_size" | bc -l) )); then + log_error "Internal drive ($internal_size GB) is smaller than LVM structure ($vg_size GB)" + exit 1 + fi + + log_info "Safety checks passed" +} + +# Create backup directory +create_backup_dir() { + log_step "Creating backup directory..." + mkdir -p "$BACKUP_DIR" + + # Backup current LVM configuration + vgcfgbackup -f "$BACKUP_DIR/vg_backup" "$VG_NAME" + + # Save partition tables + sfdisk -d "$EXTERNAL_DRIVE" > "$BACKUP_DIR/external_partition_table.txt" + sfdisk -d "$INTERNAL_DRIVE" > "$BACKUP_DIR/internal_partition_table.txt" 2>/dev/null || true + + log_info "Backup directory created at $BACKUP_DIR" +} + +# Wipe internal drive and create new partition structure +prepare_internal_drive() { + log_step "Preparing internal drive..." + + # Confirmation + echo -e "${RED}WARNING: This will completely wipe $INTERNAL_DRIVE${NC}" + echo "Current internal drive content will be lost!" + read -p "Type 'YES' to continue: " confirm + if [[ "$confirm" != "YES" ]]; then + log_error "Migration cancelled by user" + exit 1 + fi + + # Unmount any mounted partitions from internal drive + for mount in $(findmnt -n -o TARGET -S "$INTERNAL_DRIVE"* 2>/dev/null || true); do + log_info "Unmounting $mount" + umount "$mount" || true + done + + # Deactivate any LVM on internal drive + for pv in $(pvs --noheadings -o pv_name | grep -E "^[[:space:]]*$INTERNAL_DRIVE" || true); do + log_info "Deactivating PV $pv" + vgchange -an $(pvs --noheadings -o vg_name "$pv") || true + done + + # Wipe filesystem signatures and partition table + wipefs -af "$INTERNAL_DRIVE" + + # Create new GPT partition table + parted "$INTERNAL_DRIVE" --script mklabel gpt + + # Create EFI boot partition (512MB) + parted "$INTERNAL_DRIVE" --script mkpart primary fat32 1MiB 513MiB + parted "$INTERNAL_DRIVE" --script set 1 esp on + + # Create LVM partition (rest of the drive) + parted "$INTERNAL_DRIVE" --script mkpart primary 513MiB 100% + parted "$INTERNAL_DRIVE" --script set 2 lvm on + + # Wait for partitions to be recognized + sleep 2 + partprobe "$INTERNAL_DRIVE" + sleep 2 + + # Format EFI partition + mkfs.fat -F32 "${INTERNAL_DRIVE}p1" + + log_info "Internal drive prepared with new partition structure" +} + +# Create LVM structure on internal drive +create_lvm_structure() { + log_step "Creating LVM structure on internal drive..." + + # Create physical volume + pvcreate "${INTERNAL_DRIVE}p2" + + # Create volume group + vgcreate "$NEW_VG_NAME" "${INTERNAL_DRIVE}p2" + + # Get sizes from external LVM + root_size=$(lvs --noheadings --units g --nosuffix -o lv_size "$VG_NAME/root" | tr -d ' ') + home_size=$(lvs --noheadings --units g --nosuffix -o lv_size "$VG_NAME/home" | tr -d ' ') + boot_size=$(lvs --noheadings --units g --nosuffix -o lv_size "$VG_NAME/boot" | tr -d ' ') + swap_size=$(lvs --noheadings --units g --nosuffix -o lv_size "$VG_NAME/swap" | tr -d ' ') + + # Reserve 20% free space for snapshots + vg_free=$(vgs --noheadings --units g --nosuffix -o vg_free "$NEW_VG_NAME" | tr -d ' ') + snapshot_reserve=$(echo "$vg_free * 0.2" | bc -l | cut -d. -f1) + + log_info "Reserving ${snapshot_reserve}GB for LVM snapshots" + + # Create logical volumes with same sizes as external + lvcreate -L "${root_size}G" -n root "$NEW_VG_NAME" + lvcreate -L "${boot_size}G" -n boot "$NEW_VG_NAME" + lvcreate -L "${swap_size}G" -n swap "$NEW_VG_NAME" + + # Create home with remaining space minus snapshot reserve + remaining_space=$(echo "$vg_free - $snapshot_reserve" | bc -l | cut -d. -f1) + adjusted_home_size=$(echo "$remaining_space - $root_size - $boot_size - $swap_size" | bc -l | cut -d. -f1) + + if (( $(echo "$adjusted_home_size > 0" | bc -l) )); then + lvcreate -L "${adjusted_home_size}G" -n home "$NEW_VG_NAME" + else + log_warn "Using original home size, may reduce snapshot space" + lvcreate -L "${home_size}G" -n home "$NEW_VG_NAME" + fi + + # Create filesystems + mkfs.ext4 -L root "/dev/$NEW_VG_NAME/root" + mkfs.ext4 -L boot "/dev/$NEW_VG_NAME/boot" + mkfs.ext4 -L home "/dev/$NEW_VG_NAME/home" + mkswap -L swap "/dev/$NEW_VG_NAME/swap" + + log_info "LVM structure created on internal drive" +} + +# Mount internal drive and copy data +copy_data() { + log_step "Copying data from external to internal drive..." + + # Create mount points + mkdir -p /mnt/internal_root + mkdir -p /mnt/internal_boot + mkdir -p /mnt/internal_home + + # Mount internal LVM + mount "/dev/$NEW_VG_NAME/root" /mnt/internal_root + mount "/dev/$NEW_VG_NAME/boot" /mnt/internal_boot + mount "/dev/$NEW_VG_NAME/home" /mnt/internal_home + + # Copy data with progress + log_info "Copying root filesystem..." + rsync -avHAXS --progress / /mnt/internal_root/ \ + --exclude=/dev/* \ + --exclude=/proc/* \ + --exclude=/sys/* \ + --exclude=/tmp/* \ + --exclude=/run/* \ + --exclude=/mnt/* \ + --exclude=/media/* \ + --exclude=/lost+found \ + --exclude=/boot/* + + log_info "Copying boot filesystem..." + rsync -avHAXS --progress /boot/ /mnt/internal_boot/ + + log_info "Copying home filesystem..." + rsync -avHAXS --progress /home/ /mnt/internal_home/ + + log_info "Data copy completed" +} + +# Configure system for internal LVM boot +configure_boot() { + log_step "Configuring boot for internal LVM..." + + # Mount EFI partition + mkdir -p /mnt/internal_root/boot/efi + mount "${INTERNAL_DRIVE}p1" /mnt/internal_root/boot/efi + + # Chroot preparations + mount --bind /dev /mnt/internal_root/dev + mount --bind /proc /mnt/internal_root/proc + mount --bind /sys /mnt/internal_root/sys + mount --bind /run /mnt/internal_root/run + + # Update fstab + cat > /mnt/internal_root/etc/fstab << EOF +# Internal LVM Configuration +/dev/$NEW_VG_NAME/root / ext4 defaults 0 1 +/dev/$NEW_VG_NAME/boot /boot ext4 defaults 0 2 +/dev/$NEW_VG_NAME/home /home ext4 defaults 0 2 +/dev/$NEW_VG_NAME/swap none swap sw 0 0 +${INTERNAL_DRIVE}p1 /boot/efi vfat umask=0077 0 1 +EOF + + # Update initramfs to include LVM + chroot /mnt/internal_root /bin/bash -c "update-initramfs -u -k all" + + # Install and configure GRUB + chroot /mnt/internal_root /bin/bash -c "grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=debian" + chroot /mnt/internal_root /bin/bash -c "update-grub" + + # Cleanup mounts + umount /mnt/internal_root/dev + umount /mnt/internal_root/proc + umount /mnt/internal_root/sys + umount /mnt/internal_root/run + umount /mnt/internal_root/boot/efi + + log_info "Boot configuration completed" +} + +# Setup LVM snapshots +setup_snapshots() { + log_step "Setting up LVM snapshot capability..." + + # Create snapshot management script + cat > /mnt/internal_root/usr/local/bin/lvm-snapshot-manager << 'EOF' +#!/bin/bash +# LVM Snapshot Manager + +VG_NAME="internal-vg" +SNAPSHOT_SIZE="10G" # Adjust based on available space + +case "$1" in + create) + echo "Creating LVM snapshots..." + lvcreate -L $SNAPSHOT_SIZE -s -n root_snapshot $VG_NAME/root + lvcreate -L $SNAPSHOT_SIZE -s -n home_snapshot $VG_NAME/home + echo "Snapshots created successfully" + ;; + remove) + echo "Removing LVM snapshots..." + lvremove -f $VG_NAME/root_snapshot 2>/dev/null || true + lvremove -f $VG_NAME/home_snapshot 2>/dev/null || true + echo "Snapshots removed" + ;; + list) + echo "Current snapshots:" + lvs $VG_NAME | grep snapshot || echo "No snapshots found" + ;; + merge) + if [[ -z "$2" ]]; then + echo "Usage: $0 merge " + exit 1 + fi + echo "Merging snapshot $2..." + lvconvert --merge $VG_NAME/$2 + echo "Snapshot merge initiated (requires reboot to complete)" + ;; + *) + echo "Usage: $0 {create|remove|list|merge }" + echo " create - Create snapshots of root and home" + echo " remove - Remove all snapshots" + echo " list - List existing snapshots" + echo " merge - Merge a snapshot back to origin" + exit 1 + ;; +esac +EOF + + chmod +x /mnt/internal_root/usr/local/bin/lvm-snapshot-manager + + # Create backup script using snapshots + cat > /mnt/internal_root/usr/local/bin/snapshot-backup << 'EOF' +#!/bin/bash +# Snapshot-based backup script + +BACKUP_DIR="/backup" +VG_NAME="internal-vg" +DATE=$(date +%Y%m%d_%H%M%S) + +# Create snapshots +echo "Creating snapshots..." +/usr/local/bin/lvm-snapshot-manager create + +# Mount snapshots and backup +mkdir -p $BACKUP_DIR/root_$DATE +mkdir -p $BACKUP_DIR/home_$DATE + +mount /dev/$VG_NAME/root_snapshot $BACKUP_DIR/root_$DATE +mount /dev/$VG_NAME/home_snapshot $BACKUP_DIR/home_$DATE + +echo "Snapshots mounted. You can now backup from:" +echo " Root: $BACKUP_DIR/root_$DATE" +echo " Home: $BACKUP_DIR/home_$DATE" +echo "" +echo "When done, run: umount $BACKUP_DIR/root_$DATE $BACKUP_DIR/home_$DATE" +echo "Then run: /usr/local/bin/lvm-snapshot-manager remove" +EOF + + chmod +x /mnt/internal_root/usr/local/bin/snapshot-backup + + log_info "LVM snapshot tools installed" +} + +# Cleanup and unmount +cleanup() { + log_step "Cleaning up..." + + # Unmount all internal mounts + umount /mnt/internal_home || true + umount /mnt/internal_boot || true + umount /mnt/internal_root || true + + # Remove mount points + rmdir /mnt/internal_home /mnt/internal_boot /mnt/internal_root 2>/dev/null || true + + log_info "Cleanup completed" +} + +# Main migration function +main() { + log_info "Starting LVM migration from external M.2 to internal NVMe" + log_info "External: $EXTERNAL_DRIVE -> Internal: $INTERNAL_DRIVE" + + safety_checks + create_backup_dir + prepare_internal_drive + create_lvm_structure + copy_data + configure_boot + setup_snapshots + cleanup + + log_info "Migration completed successfully!" + echo + echo -e "${GREEN}SUCCESS!${NC} LVM migration completed" + echo "Next steps:" + echo "1. Reboot and select internal drive in BIOS/UEFI" + echo "2. Verify system boots correctly from internal LVM" + echo "3. Test snapshot functionality with: sudo lvm-snapshot-manager create" + echo "4. Once confirmed working, you can repurpose the external M.2" + echo + echo "Snapshot management commands:" + echo " sudo lvm-snapshot-manager create # Create snapshots" + echo " sudo lvm-snapshot-manager list # List snapshots" + echo " sudo lvm-snapshot-manager remove # Remove snapshots" + echo " sudo snapshot-backup # Backup using snapshots" +} + +# Handle interruption +trap cleanup EXIT + +# Run main function +main "$@" \ No newline at end of file diff --git a/preview_migration.sh b/preview_migration.sh new file mode 100755 index 0000000..d05a242 --- /dev/null +++ b/preview_migration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# LVM Migration Preview Script +# Shows current status and what the migration will do + +set -euo pipefail + +# Colors +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +RED='\033[0;31m' +NC='\033[0m' + +echo -e "${BLUE}=== LVM Migration Preview ===${NC}" +echo + +echo -e "${GREEN}Current Boot Status:${NC}" +echo "Root mounted from: $(findmnt -n -o SOURCE /)" +echo "Boot mounted from: $(findmnt -n -o SOURCE /boot)" +echo "Home mounted from: $(findmnt -n -o SOURCE /home)" +echo + +echo -e "${GREEN}Current LVM Structure (External M.2):${NC}" +sudo vgs migration-vg 2>/dev/null || echo "No VG found" +echo +sudo lvs migration-vg 2>/dev/null || echo "No LVs found" +echo + +echo -e "${GREEN}Current Drive Layout:${NC}" +echo "External M.2 (sda):" +lsblk /dev/sda +echo +echo "Internal NVMe (nvme0n1):" +lsblk /dev/nvme0n1 +echo + +echo -e "${GREEN}Available Space Analysis:${NC}" +echo "External VG size: $(sudo vgs --noheadings --units g -o vg_size migration-vg | tr -d ' ')B" +echo "External VG free: $(sudo vgs --noheadings --units g -o vg_free migration-vg | tr -d ' ')B" +echo "Internal drive size: $(lsblk -b -n -o SIZE /dev/nvme0n1 | awk '{printf "%.1fGB", $1/1024/1024/1024}')" +echo + +echo -e "${YELLOW}Migration Plan:${NC}" +echo "1. ✅ Wipe internal NVMe drive completely" +echo "2. ✅ Create new GPT partition table" +echo "3. ✅ Create EFI boot partition (512MB)" +echo "4. ✅ Create LVM partition (remaining space)" +echo "5. ✅ Set up LVM with volume group 'internal-vg'" +echo "6. ✅ Create logical volumes matching current structure" +echo "7. ✅ Copy all data from external to internal" +echo "8. ✅ Configure GRUB for LVM boot" +echo "9. ✅ Set up LVM snapshot capabilities" +echo "10. ✅ Reserve 20% space for snapshots" +echo + +echo -e "${RED}⚠️ WARNINGS:${NC}" +echo "• This will COMPLETELY WIPE the internal NVMe drive" +echo "• All current data on internal drive will be lost" +echo "• Make sure you're booted from external M.2 (verified above)" +echo "• Ensure external M.2 LED is active/blinking" +echo + +echo -e "${GREEN}Snapshot Features After Migration:${NC}" +echo "• lvm-snapshot-manager create - Create system snapshots" +echo "• lvm-snapshot-manager list - List existing snapshots" +echo "• lvm-snapshot-manager remove - Remove snapshots" +echo "• lvm-snapshot-manager merge - Restore from snapshot" +echo "• snapshot-backup - Backup using snapshots" +echo + +echo -e "${BLUE}Ready to proceed?${NC}" +echo "Run: sudo ./migrate_lvm_to_internal.sh" +echo +echo -e "${YELLOW}Estimated time: 2-4 hours depending on data size${NC}" \ No newline at end of file diff --git a/preview_snapshot_migration.sh b/preview_snapshot_migration.sh new file mode 100755 index 0000000..8badf03 --- /dev/null +++ b/preview_snapshot_migration.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +# Snapshot Migration Preview Script +# Shows what the snapshot-based migration will do + +set -euo pipefail + +# Colors +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +RED='\033[0;31m' +NC='\033[0m' + +echo -e "${BLUE}=== Snapshot-Based LVM Migration Preview ===${NC}" +echo + +echo -e "${GREEN}Current System Status:${NC}" +echo "✅ Booted from: $(findmnt -n -o SOURCE /)" +echo "✅ Volume Group: migration-vg" +echo + +echo -e "${GREEN}Current Logical Volumes:${NC}" +sudo lvs migration-vg --units g -o lv_name,lv_size,data_percent 2>/dev/null +echo + +echo -e "${GREEN}Available Space for Snapshots:${NC}" +free_space=$(sudo vgs --noheadings --units m --nosuffix -o vg_free migration-vg | tr -d ' ' | tr ',' '.') +if command -v bc >/dev/null 2>&1; then + snapshot_size_per_lv=$(echo "$free_space * 0.8 / 4" | bc 2>/dev/null | cut -d. -f1) +else + snapshot_size_per_lv=$((${free_space%.*} * 80 / 100 / 4)) +fi +echo "Free space: ${free_space}MB" +echo "Snapshot size per LV: ${snapshot_size_per_lv}MB" + +if (( snapshot_size_per_lv < 100 )); then + echo -e "${RED}⚠️ WARNING: Limited space for snapshots!${NC}" + echo "Consider freeing up space or expanding the volume group" +else + echo -e "${GREEN}✅ Sufficient space for snapshots${NC}" +fi +echo + +echo -e "${GREEN}Target Drive Analysis:${NC}" +echo "Internal NVMe (nvme0n1):" +lsblk /dev/nvme0n1 +echo "Size: $(lsblk -b -n -o SIZE /dev/nvme0n1 | awk '{printf "%.1fGB", $1/1024/1024/1024}')" +echo + +echo -e "${YELLOW}Snapshot Migration Process:${NC}" +echo "1. 📸 Create temporary snapshots of ALL logical volumes:" +echo " • root (${snapshot_size_per_lv}MB snapshot)" +echo " • home (${snapshot_size_per_lv}MB snapshot)" +echo " • boot (${snapshot_size_per_lv}MB snapshot)" +echo " • swap (${snapshot_size_per_lv}MB snapshot)" +echo +echo "2. 🔧 Prepare internal drive:" +echo " • Copy partition table from external M.2" +echo " • Create LVM physical volume" +echo " • Create volume group 'internal-vg'" +echo " • Create logical volumes with exact same sizes" +echo +echo "3. 📋 Transfer data using block-level copy:" +echo " • Use dd to copy each snapshot to internal LV" +echo " • Process one LV at a time to manage space" +echo " • Remove each snapshot after successful copy" +echo +echo "4. ⚙️ Configure boot:" +echo " • Update fstab for new volume group" +echo " • Install GRUB on internal drive" +echo " • Update initramfs for LVM" +echo +echo "5. 🛠️ Setup snapshot tools:" +echo " • Install lvm-snapshot-manager" +echo " • Reserve space for future snapshots" +echo + +echo -e "${RED}⚠️ CRITICAL WARNINGS:${NC}" +echo "• Internal NVMe will be COMPLETELY WIPED" +echo "• This creates an EXACT copy including all current data" +echo "• Process takes 1-3 hours depending on data size" +echo "• Snapshots use limited available space" +echo + +echo -e "${GREEN}Advantages of Snapshot Method:${NC}" +echo "✅ Exact bit-for-bit copy of live system" +echo "✅ No filesystem corruption risks" +echo "✅ Can capture running system state" +echo "✅ Uses LVM native capabilities" +echo "✅ Block-level transfer (faster than file copy)" +echo + +echo -e "${GREEN}Post-Migration Capabilities:${NC}" +echo "• sudo lvm-snapshot-manager create - Create system snapshots" +echo "• sudo lvm-snapshot-manager list - Show snapshots" +echo "• sudo lvm-snapshot-manager remove - Clean up snapshots" +echo "• sudo lvm-snapshot-manager merge - Restore from snapshot" +echo + +echo -e "${BLUE}Ready to proceed with snapshot migration?${NC}" +echo "Run: sudo ./snapshot_migrate_to_internal.sh" +echo +echo -e "${YELLOW}Estimated time: 1-3 hours${NC}" \ No newline at end of file diff --git a/snapshot_migrate_to_internal.sh b/snapshot_migrate_to_internal.sh new file mode 100755 index 0000000..234e14a --- /dev/null +++ b/snapshot_migrate_to_internal.sh @@ -0,0 +1,367 @@ +#!/bin/bash + +# Snapshot-Based LVM Migration Script: External M.2 to Internal NVMe +# Creates snapshots of ALL partitions and transfers them to internal drive + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration +EXTERNAL_DRIVE="/dev/sda" +INTERNAL_DRIVE="/dev/nvme0n1" +SOURCE_VG="migration-vg" +TARGET_VG="internal-vg" +BACKUP_DIR="/tmp/lvm_snapshot_migration" + +# Logging +LOG_FILE="/var/log/lvm_snapshot_migration.log" +exec 1> >(tee -a "$LOG_FILE") +exec 2> >(tee -a "$LOG_FILE" >&2) + +log_info() { + echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1" +} + +log_step() { + echo -e "${BLUE}[STEP]${NC} $(date '+%Y-%m-%d %H:%M:%S'): $1" +} + +# Safety checks +safety_checks() { + log_step "Performing safety checks..." + + # Check if running as root + if [[ $EUID -ne 0 ]]; then + log_error "This script must be run as root (use sudo)" + exit 1 + fi + + # Verify we're booted from external drive + root_device=$(findmnt -n -o SOURCE /) + if [[ "$root_device" != "/dev/mapper/migration--vg-root" ]]; then + log_error "Not booted from external LVM! Current root: $root_device" + exit 1 + fi + + # Check drives exist + if [[ ! -b "$EXTERNAL_DRIVE" ]] || [[ ! -b "$INTERNAL_DRIVE" ]]; then + log_error "Required drives not found" + exit 1 + fi + + # Check LVM tools + if ! command -v lvcreate &> /dev/null; then + log_error "LVM tools not found" + exit 1 + fi + + log_info "Safety checks passed" +} + +# Create backup directory and metadata +create_backup_metadata() { + log_step "Creating backup metadata..." + mkdir -p "$BACKUP_DIR" + + # Backup LVM metadata + vgcfgbackup -f "$BACKUP_DIR/vg_backup" "$SOURCE_VG" + + # Save current LV information + lvs "$SOURCE_VG" > "$BACKUP_DIR/lv_info.txt" + vgs "$SOURCE_VG" > "$BACKUP_DIR/vg_info.txt" + pvs > "$BACKUP_DIR/pv_info.txt" + + # Save partition information + lsblk > "$BACKUP_DIR/lsblk_before.txt" + + log_info "Backup metadata created" +} + +# Prepare internal drive with identical structure +prepare_internal_drive() { + log_step "Preparing internal drive..." + + echo -e "${RED}WARNING: This will completely wipe $INTERNAL_DRIVE${NC}" + read -p "Type 'YES' to continue: " confirm + if [[ "$confirm" != "YES" ]]; then + log_error "Migration cancelled" + exit 1 + fi + + # Clean up any existing mounts/LVM on internal drive + for mount in $(findmnt -n -o TARGET -S "$INTERNAL_DRIVE"* 2>/dev/null || true); do + umount "$mount" || true + done + + # Deactivate any existing VGs on internal drive + for vg in $(vgs --noheadings -o vg_name 2>/dev/null | grep -v "$SOURCE_VG" || true); do + vgchange -an "$vg" 2>/dev/null || true + done + + # Wipe the drive + wipefs -af "$INTERNAL_DRIVE" + + # Create identical partition structure to external drive + log_info "Creating partition table..." + sfdisk -d "$EXTERNAL_DRIVE" | sfdisk "$INTERNAL_DRIVE" + + # Wait for partitions to appear + sleep 3 + partprobe "$INTERNAL_DRIVE" + sleep 3 + + log_info "Internal drive partitioned" +} + +# Create LVM structure on internal drive +setup_internal_lvm() { + log_step "Setting up LVM on internal drive..." + + # Create physical volume on the LVM partition + pvcreate "${INTERNAL_DRIVE}p2" -ff + + # Create volume group with extra space for snapshots + vgcreate "$TARGET_VG" "${INTERNAL_DRIVE}p2" + + # Get exact sizes from source LVs + declare -A lv_sizes + while IFS= read -r line; do + lv_name=$(echo "$line" | awk '{print $1}') + lv_size=$(echo "$line" | awk '{print $2}') + lv_sizes["$lv_name"]="$lv_size" + done < <(lvs --noheadings --units b --nosuffix -o lv_name,lv_size "$SOURCE_VG") + + # Create LVs with exact same sizes + for lv in root home boot swap; do + if [[ -n "${lv_sizes[$lv]:-}" ]]; then + size_bytes="${lv_sizes[$lv]}" + log_info "Creating LV $lv with size $size_bytes bytes" + lvcreate -L "${size_bytes}b" -n "$lv" "$TARGET_VG" + fi + done + + log_info "Internal LVM structure created" +} + +# Create temporary snapshots and transfer via dd +transfer_with_snapshots() { + log_step "Transferring data using snapshots..." + + # Calculate optimal snapshot size (use available free space) + free_space=$(vgs --noheadings --units m --nosuffix -o vg_free "$SOURCE_VG" | tr -d ' ' | tr ',' '.') + # Use 80% of free space for snapshots, divided by number of LVs + snapshot_size=$(echo "$free_space * 0.8 / 4" | bc | cut -d. -f1) + + if (( snapshot_size < 100 )); then + log_error "Not enough free space for snapshots. Need at least 400MB free." + exit 1 + fi + + log_info "Using ${snapshot_size}MB for each snapshot" + + # Process each logical volume + for lv in root home boot swap; do + log_step "Processing $lv..." + + # Create snapshot + log_info "Creating snapshot of $lv..." + if ! lvcreate -L "${snapshot_size}M" -s -n "${lv}_snapshot" "$SOURCE_VG/$lv"; then + log_error "Failed to create snapshot for $lv" + continue + fi + + # Copy data using dd with progress + log_info "Copying $lv data to internal drive..." + if [[ "$lv" == "swap" ]]; then + # For swap, just copy the LV structure + dd if="/dev/$SOURCE_VG/${lv}_snapshot" of="/dev/$TARGET_VG/$lv" bs=1M status=progress + else + # For filesystems, use dd for exact copy + dd if="/dev/$SOURCE_VG/${lv}_snapshot" of="/dev/$TARGET_VG/$lv" bs=1M status=progress + fi + + # Remove snapshot to free space for next one + log_info "Removing snapshot of $lv..." + lvremove -f "$SOURCE_VG/${lv}_snapshot" + + log_info "Completed transfer of $lv" + done + + log_info "All data transferred successfully" +} + +# Setup boot for internal drive +configure_internal_boot() { + log_step "Configuring boot for internal drive..." + + # Format and mount EFI partition + mkfs.fat -F32 "${INTERNAL_DRIVE}p1" || true + mkdir -p /mnt/internal_efi + mount "${INTERNAL_DRIVE}p1" /mnt/internal_efi + + # Mount internal filesystems + mkdir -p /mnt/internal_root /mnt/internal_boot /mnt/internal_home + mount "/dev/$TARGET_VG/root" /mnt/internal_root + mount "/dev/$TARGET_VG/boot" /mnt/internal_boot + mount "/dev/$TARGET_VG/home" /mnt/internal_home + + # Mount EFI in the target system + mkdir -p /mnt/internal_root/boot/efi + mount "${INTERNAL_DRIVE}p1" /mnt/internal_root/boot/efi + + # Update fstab for new VG + log_info "Updating fstab..." + cat > /mnt/internal_root/etc/fstab << EOF +# Internal LVM Configuration +/dev/$TARGET_VG/root / ext4 defaults 0 1 +/dev/$TARGET_VG/boot /boot ext4 defaults 0 2 +/dev/$TARGET_VG/home /home ext4 defaults 0 2 +/dev/$TARGET_VG/swap none swap sw 0 0 +${INTERNAL_DRIVE}p1 /boot/efi vfat umask=0077 0 1 +EOF + + # Prepare chroot environment + mount --bind /dev /mnt/internal_root/dev + mount --bind /proc /mnt/internal_root/proc + mount --bind /sys /mnt/internal_root/sys + mount --bind /run /mnt/internal_root/run + + # Update initramfs and install GRUB + log_info "Updating initramfs and GRUB..." + chroot /mnt/internal_root /bin/bash -c "update-initramfs -u -k all" + chroot /mnt/internal_root /bin/bash -c "grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=Internal-LVM" + chroot /mnt/internal_root /bin/bash -c "update-grub" + + # Clean up mounts + umount /mnt/internal_root/dev /mnt/internal_root/proc /mnt/internal_root/sys /mnt/internal_root/run + umount /mnt/internal_root/boot/efi + umount /mnt/internal_efi /mnt/internal_root /mnt/internal_boot /mnt/internal_home + + log_info "Boot configuration completed" +} + +# Setup snapshot capabilities on internal drive +setup_snapshot_tools() { + log_step "Setting up snapshot tools on internal drive..." + + # Remount for tool installation + mount "/dev/$TARGET_VG/root" /mnt/internal_root + + # Create snapshot management script + cat > /mnt/internal_root/usr/local/bin/lvm-snapshot-manager << 'EOFSCRIPT' +#!/bin/bash +# LVM Snapshot Manager for Internal Drive + +VG_NAME="internal-vg" + +case "$1" in + create) + echo "Creating LVM snapshots..." + # Calculate available space for snapshots + free_space=$(vgs --noheadings --units g --nosuffix -o vg_free "$VG_NAME" | tr -d ' ') + snapshot_size=$(echo "$free_space / 4" | bc)G + + lvcreate -L "$snapshot_size" -s -n root_backup "$VG_NAME/root" + lvcreate -L "$snapshot_size" -s -n home_backup "$VG_NAME/home" + lvcreate -L "$snapshot_size" -s -n boot_backup "$VG_NAME/boot" + echo "Snapshots created with size: $snapshot_size each" + ;; + remove) + echo "Removing LVM snapshots..." + lvremove -f "$VG_NAME/root_backup" 2>/dev/null || true + lvremove -f "$VG_NAME/home_backup" 2>/dev/null || true + lvremove -f "$VG_NAME/boot_backup" 2>/dev/null || true + echo "Snapshots removed" + ;; + list) + echo "Current snapshots:" + lvs "$VG_NAME" | grep backup || echo "No snapshots found" + ;; + merge) + if [[ -z "$2" ]]; then + echo "Usage: $0 merge " + exit 1 + fi + echo "Merging snapshot $2..." + lvconvert --merge "$VG_NAME/$2" + echo "Snapshot merge initiated (reboot required to complete)" + ;; + *) + echo "Usage: $0 {create|remove|list|merge }" + exit 1 + ;; +esac +EOFSCRIPT + + chmod +x /mnt/internal_root/usr/local/bin/lvm-snapshot-manager + + umount /mnt/internal_root + + log_info "Snapshot tools installed" +} + +# Cleanup function +cleanup() { + log_info "Cleaning up..." + + # Remove any remaining snapshots + for snap in $(lvs --noheadings -o lv_name "$SOURCE_VG" 2>/dev/null | grep snapshot || true); do + lvremove -f "$SOURCE_VG/$snap" 2>/dev/null || true + done + + # Unmount any remaining mounts + for mount in /mnt/internal_*; do + umount "$mount" 2>/dev/null || true + rmdir "$mount" 2>/dev/null || true + done +} + +# Main migration function +main() { + log_info "Starting snapshot-based LVM migration" + log_info "Source: $EXTERNAL_DRIVE ($SOURCE_VG) -> Target: $INTERNAL_DRIVE ($TARGET_VG)" + + safety_checks + create_backup_metadata + prepare_internal_drive + setup_internal_lvm + transfer_with_snapshots + configure_internal_boot + setup_snapshot_tools + cleanup + + log_info "Migration completed successfully!" + echo + echo -e "${GREEN}SUCCESS!${NC} Snapshot-based LVM migration completed" + echo + echo "Next steps:" + echo "1. Reboot and select internal drive in BIOS/UEFI" + echo "2. Verify all systems working from internal LVM" + echo "3. Test snapshot functionality: sudo lvm-snapshot-manager create" + echo "4. External M.2 can now be used as backup drive" + echo + echo "The internal drive now has:" + echo "- Complete copy of your current system" + echo "- LVM with snapshot capabilities" + echo "- Reserved space for future snapshots" +} + +# Handle interruption +trap cleanup EXIT + +# Run migration +main "$@" \ No newline at end of file