#!/bin/bash # RAID Configuration Script for Raspberry Pi / Script de Configuración RAID para Raspberry Pi # Supports BTRFS and ZFS / Soporta BTRFS y ZFS set -e # Colors for output / Colores para output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Global variable for language selection SCRIPT_LANGUAGE="en" export SCRIPT_LANGUAGE # Language selection function / Función de selección de idioma select_language() { clear echo "╔════════════════════════════════════════════════════════════════╗" echo "║ RAID Configuration Script ║" echo "║ Script de Configuración RAID ║" echo "╚════════════════════════════════════════════════════════════════╝" echo "" echo "Please select your preferred language / Por favor selecciona tu idioma preferido:" echo "" echo " 1. English" echo " 2. Español" echo "" while true; do read -p "Language choice / Elección de idioma (1-2): " lang_choice case $lang_choice in 1) SCRIPT_LANGUAGE="en" export SCRIPT_LANGUAGE echo "Language set to English" break ;; 2) SCRIPT_LANGUAGE="es" export SCRIPT_LANGUAGE echo "Idioma establecido en Español" break ;; *) echo "Invalid option. Please select 1 or 2 / Opción inválida. Selecciona 1 o 2" ;; esac done echo "" sleep 1 } # Multilingual text messages / Mensajes de texto multiidioma get_text() { local key="$1" case "$key" in "script_title") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "RAID Configuration Script for Raspberry Pi" else echo "Script de Configuración RAID para Raspberry Pi" fi ;; "root_warning") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Do not run this script as root. Use sudo when necessary." else echo "No ejecutes este script como root. Usa sudo cuando sea necesario." fi ;; "checking_requirements") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Checking System Requirements" else echo "Verificando Requisitos del Sistema" fi ;; "updating_packages") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Updating package list..." else echo "Actualizando lista de paquetes..." fi ;; "btrfs_available") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "✓ BTRFS: Available" else echo "✓ BTRFS: Disponible" fi ;; "btrfs_not_available") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "✗ BTRFS: Not available" else echo "✗ BTRFS: No disponible" fi ;; "zfs_available") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "✓ ZFS: Available" else echo "✓ ZFS: Disponible" fi ;; "zfs_not_available") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "✗ ZFS: Not available" else echo "✗ ZFS: No disponible" fi ;; "no_raid_tools") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "No RAID tools available in the system" else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "No RAID tools available on the system" else echo "No hay herramientas RAID disponibles en el sistema" fi fi ;; "will_install_packages") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Required packages will be installed..." else echo "Se instalarán los paquetes necesarios..." fi ;; "packages_to_install") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Packages to be installed:" else echo "Paquetes que se instalarán:" fi ;; "zfs_install_warning") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "⚠️ ZFS may take up to 10 minutes to install due to kernel module compilation" else echo "⚠️ ZFS puede tardar hasta 10 minutos en instalarse debido a la compilación de módulos del kernel" fi ;; "installation_progress_shown") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "⚠️ You will see the installation progress on screen" else echo "⚠️ Durante la instalación verás el progreso en pantalla" fi ;; "continue_installation") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Do you want to continue with the installation?" else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Do you want to continue with the installation?" else echo "¿Deseas continuar con la instalación?" fi fi ;; "installation_cancelled") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Installation cancelled by user" else echo "Instalación cancelada por el usuario" fi ;; "starting_installation") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Starting package installation..." else echo "Iniciando instalación de paquetes..." fi ;; "installing_zfs") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "🔄 Installing ZFS (this may take several minutes)..." else echo "🔄 Instalando ZFS (esto puede tomar varios minutos)..." fi ;; "zfs_progress") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "📦 ZFS Installation Progress:" else echo "📦 Progreso de instalación ZFS:" fi ;; "zfs_installed_successfully") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "✅ ZFS installed successfully" else echo "✅ ZFS instalado exitosamente" fi ;; "error_installing_zfs") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "❌ Error installing ZFS" else echo "❌ Error instalando ZFS" fi ;; "please_respond_yn") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Please answer y or n." else echo "Por favor responde y o n." fi ;; "installation_cancelled") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Installation cancelled by user" else echo "Instalación cancelada por el usuario" fi ;; "raid_completed") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "RAID configuration completed successfully!" else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "¡RAID configuration completed successfully!" else echo "¡Configuración RAID completada exitosamente!" fi fi ;; "raid_mounted_at") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "RAID is mounted at:" else echo "El RAID está montado en:" fi ;; "detect_existing_raid") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Detecting Existing RAID Configurations" else echo "Detección de Configuraciones RAID Existentes" fi ;; "available_options") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "🛠️ AVAILABLE OPTIONS:" else echo "🛠️ OPCIONES DISPONIBLES:" fi ;; "continue_new_raid") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Continue with new RAID configuration" else echo "Continuar con configuración de nuevo RAID" fi ;; "exit_script") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Exit script" else echo "Salir del script" fi ;; "no_raid_detected") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "ℹ️ No existing RAID configurations detected." else echo "ℹ️ No se detectaron configuraciones RAID existentes." fi ;; "system_ready_new_raid") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " System is ready to configure a new RAID." else echo " El sistema está listo para configurar un nuevo RAID." fi ;; "manage_zfs_pools") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Manage ZFS pools and datasets" else echo "Gestionar pools y datasets ZFS" fi ;; "delete_zfs_pools") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Delete specific ZFS pools" else echo "Eliminar pools ZFS específicos" fi ;; "delete_btrfs_filesystems") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Delete specific BTRFS filesystems" else echo "Eliminar filesystems BTRFS específicos" fi ;; "manage_mdadm_arrays") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Manage MDADM arrays (information only)" else echo "Gestionar arrays MDADM (información solamente)" fi ;; "manage_lvm_groups") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Manage LVM Volume Groups (information only)" else echo "Gestionar Volume Groups LVM (información solamente)" fi ;; "continue_new_raid") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Continue with new RAID configuration" else echo "Continuar con configuración de nuevo RAID" fi ;; "exit_script") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Exit script" else echo "Salir del script" fi ;; "invalid_option") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "❌ Invalid option. Please select a valid number." else echo "❌ Opción inválida. Selecciona un número válido." fi ;; "select_option") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "👉 Select an option" else echo "👉 Selecciona una opción" fi ;; "continuing_new_raid") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Continuing with new RAID configuration..." else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Continuing with new RAID configuration..." else echo "Continuando con configuración de nuevo RAID..." fi fi ;; "exiting_script") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Exiting script..." else echo "Saliendo del script..." fi ;; "manage_mdadm_arrays") if [ "$SCRIPT_LANGUAGE" = "es" ]; then echo "Gestionar arrays MDADM (información solamente)" else echo "Manage MDADM arrays (information only)" fi ;; "manage_lvm_groups") if [ "$SCRIPT_LANGUAGE" = "es" ]; then echo "Gestionar Volume Groups LVM (información solamente)" else echo "Manage LVM Volume Groups (information only)" fi ;; "delete_btrfs_filesystems") if [ "$SCRIPT_LANGUAGE" = "es" ]; then echo "Eliminar filesystems BTRFS específicos" else echo "Delete specific BTRFS filesystems" fi ;; "mdadm_management_info") if [ "$SCRIPT_LANGUAGE" = "es" ]; then echo "Para gestionar arrays MDADM, usa herramientas específicas como:" else echo "To manage MDADM arrays, use specific tools like:" fi ;; "lvm_management_info") if [ "$SCRIPT_LANGUAGE" = "es" ]; then echo "Para gestionar LVM, usa herramientas específicas como:" else echo "To manage LVM, use specific tools like:" fi ;; "continue_detection") if [ "$SCRIPT_LANGUAGE" = "es" ]; then echo "¿Continuar con la detección de configuraciones?" else echo "Continue with configuration detection?" fi ;; "sector_config_detected") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "[INFO] 📊 Detected sector configuration:" else echo "[INFO] 📊 Configuración de sectores detectada:" fi ;; "max_sector_size_hdds") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "[INFO] Maximum sector size in HDDs:" else echo "[INFO] Tamaño de sector máximo en HDDs:" fi ;; "pool_ashift") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "[INFO] Pool ashift:" else echo "[INFO] Ashift del pool:" fi ;; "compatibility_cache_devices") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "[INFO] ✅ This ensures compatibility with SSD cache devices (4096 bytes)" else echo "[INFO] ✅ Esto garantiza compatibilidad con cache devices SSD (4096 bytes)" fi ;; "config_for_pool") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "[INFO] 📊 Configuration for pool" else echo "[INFO] 📊 Configuración para pool" fi ;; "ensures_compatibility_future") if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "[INFO] ✅ Ensures compatibility with future cache devices" else echo "[INFO] ✅ Garantiza compatibilidad con cache devices futuro" fi ;; *) echo "$key" # Fallback to key itself ;; esac } # Functions for displaying messages / Funciones para mostrar mensajes show_message() { echo -e "${GREEN}[INFO]${NC} $1" } show_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } show_error() { echo -e "${RED}[ERROR]${NC} $1" } show_title() { echo -e "${BLUE}=== $1 ===${NC}" } # Function to get user confirmation / Función para obtener confirmación del usuario confirm() { local prompt="$1" local yn_text if [ "$SCRIPT_LANGUAGE" = "en" ]; then yn_text="(y/n)" else yn_text="(s/n)" fi while true; do read -p "$prompt $yn_text: " yn case $yn in [YySs]* ) return 0;; [Nn]* ) return 1;; * ) echo "$(get_text "please_respond_yn")";; esac done } # Function to check and install system requirements / Función para verificar e instalar requisitos del sistema check_and_install_requirements() { show_title "$(get_text "checking_requirements")" # Update package list / Actualizar lista de paquetes show_message "$(get_text "updating_packages")" sudo apt update > /dev/null 2>&1 local packages_to_install=() local missing_packages="" # Check basic tools / Verificar herramientas básicas if ! command -v lsblk &> /dev/null; then packages_to_install+=("util-linux") missing_packages="$missing_packages util-linux" fi if ! command -v parted &> /dev/null; then packages_to_install+=("parted") missing_packages="$missing_packages parted" fi if ! command -v wipefs &> /dev/null; then packages_to_install+=("util-linux") missing_packages="$missing_packages util-linux" fi # Check BTRFS / Verificar BTRFS local btrfs_available=false if command -v mkfs.btrfs &> /dev/null && command -v btrfs &> /dev/null; then btrfs_available=true show_message "$(get_text "btrfs_available")" else show_warning "$(get_text "btrfs_not_available")" packages_to_install+=("btrfs-progs") missing_packages="$missing_packages btrfs-progs" fi # Check ZFS / Verificar ZFS local zfs_available=false if command -v zpool &> /dev/null && command -v zfs &> /dev/null; then zfs_available=true show_message "$(get_text "zfs_available")" else show_warning "$(get_text "zfs_not_available")" packages_to_install+=("zfsutils-linux") missing_packages="$missing_packages zfsutils-linux" fi # Check mdadm (useful for cleanup) / Verificar mdadm (útil para limpieza) if ! command -v mdadm &> /dev/null; then packages_to_install+=("mdadm") missing_packages="$missing_packages mdadm" fi # If no RAID tools available, we cannot continue / Si no hay herramientas RAID disponibles, no podemos continuar if [ "$btrfs_available" = false ] && [ "$zfs_available" = false ]; then show_error "$(get_text "no_raid_tools")" show_message "$(get_text "will_install_packages")" fi # Install missing packages if necessary / Instalar paquetes faltantes si es necesario if [ ${#packages_to_install[@]} -gt 0 ]; then show_message "$(get_text "packages_to_install")$missing_packages" # Check if ZFS is in the list / Verificar si ZFS está en la lista local installing_zfs=false for package in "${packages_to_install[@]}"; do if [ "$package" = "zfsutils-linux" ]; then installing_zfs=true break fi done if [ "$installing_zfs" = true ]; then show_warning "$(get_text "zfs_install_warning")" show_warning "$(get_text "installation_progress_shown")" if ! confirm "$(get_text "continue_installation")"; then show_message "$(get_text "installation_cancelled")" exit 0 fi fi show_message "$(get_text "starting_installation")" echo "----------------------------------------" # Install packages with visible output / Instalar paquetes con output visible for package in "${packages_to_install[@]}"; do if [ "$package" = "zfsutils-linux" ]; then show_message "$(get_text "installing_zfs")" echo "$(get_text "zfs_progress")" if sudo apt install -y "$package"; then show_message "$(get_text "zfs_installed_successfully")" else show_error "$(get_text "error_installing_zfs")" exit 1 fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "🔄 Installing $package..." if sudo apt install -y "$package" > /dev/null 2>&1; then show_message "✅ $package installed successfully" else show_error "❌ Error installing $package" exit 1 fi else show_message "🔄 Instalando $package..." if sudo apt install -y "$package" > /dev/null 2>&1; then show_message "✅ $package instalado exitosamente" else show_error "❌ Error instalando $package" exit 1 fi fi fi done echo "----------------------------------------" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ All packages installed successfully" else show_message "✅ Todos los paquetes se instalaron correctamente" fi # Verificar que ZFS esté funcionando si se instaló if [ "$installing_zfs" = true ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "🔄 Verifying ZFS functionality..." else show_message "🔄 Verificando funcionamiento de ZFS..." fi # Cargar módulo ZFS si no está cargado if ! lsmod | grep -q "^zfs "; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Loading ZFS module..." else show_message "Cargando módulo ZFS..." fi sudo modprobe zfs sleep 2 fi # Verificar que los comandos funcionen if zpool status > /dev/null 2>&1 && zfs version > /dev/null 2>&1; then local zfs_version=$(zfs version | head -1 | awk '{print $2}') if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ ZFS working correctly (version: $zfs_version)" else show_message "✅ ZFS funcionando correctamente (versión: $zfs_version)" fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "❌ ZFS is not working correctly" show_message "A system restart may be necessary" if confirm "Do you want to continue anyway? (may fail)"; then show_warning "Continuing with possibly non-functional ZFS..." else exit 1 fi else show_error "❌ ZFS no está funcionando correctamente" show_message "Puede ser necesario reiniciar el sistema" if confirm "¿Deseas continuar de todas formas? (puede fallar)"; then show_warning "Continuando con ZFS posiblemente no funcional..." else exit 1 fi fi fi fi # Breve pausa para que el usuario vea el resultado sleep 1 else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ All requirements are available" else show_message "✅ Todos los requisitos están disponibles" fi fi # Mostrar resumen final de herramientas disponibles if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Available RAID tools:" else show_message "Herramientas RAID disponibles:" fi if command -v mkfs.btrfs &> /dev/null; then local btrfs_version=$(btrfs --version 2>/dev/null | awk '{print $2}' || echo "desconocida") echo " ✓ BTRFS (versión: $btrfs_version)" fi if command -v zpool &> /dev/null; then local zfs_version=$(zfs version 2>/dev/null | head -1 | awk '{print $2}' || echo "desconocida") echo " ✓ ZFS (versión: $zfs_version)" fi echo "" } # Function to detect all existing RAID configurations / Función para detectar todas las configuraciones RAID existentes detect_existing_raid_configurations() { show_title "$(get_text "detect_existing_raid")" local zfs_found=false local btrfs_found=false local mdadm_found=false local lvm_found=false local any_raid_found=false # 1. DETECTAR ZFS if command -v zpool &> /dev/null; then # Verificar que el módulo ZFS esté cargado if ! lsmod | grep -q "^zfs "; then sudo modprobe zfs 2>/dev/null || true sleep 1 fi local existing_pools=$(zpool list -H -o name 2>/dev/null) if [ -n "$existing_pools" ]; then zfs_found=true any_raid_found=true echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "🔷 DETECTED ZFS POOLS:" else echo "🔷 POOLS ZFS DETECTADOS:" fi for pool in $existing_pools; do local pool_health=$(zpool list -H -o health "$pool" 2>/dev/null) local pool_size=$(zpool list -H -o size "$pool" 2>/dev/null) local pool_used=$(zpool list -H -o allocated "$pool" 2>/dev/null) local pool_free=$(zpool list -H -o free "$pool" 2>/dev/null) echo " 📦 Pool: $pool" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 💚 Status: $pool_health" echo " 📏 Size: $pool_size (Used: $pool_used, Free: $pool_free)" else echo " 💚 Estado: $pool_health" echo " 📏 Tamaño: $pool_size (Usado: $pool_used, Libre: $pool_free)" fi # Mostrar datasets existentes local datasets=$(zfs list -H -o name -r "$pool" 2>/dev/null | grep -v "^${pool}$") if [ -n "$datasets" ]; then local dataset_count=$(echo "$datasets" | wc -l) if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 📁 Datasets: $dataset_count" else echo " 📁 Datasets: $dataset_count" fi for dataset in $datasets; do local used=$(zfs list -H -o used "$dataset" 2>/dev/null) local mountpoint=$(zfs list -H -o mountpoint "$dataset" 2>/dev/null) if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " • $dataset (Used: $used, Mount: $mountpoint)" else echo " • $dataset (Usado: $used, Montaje: $mountpoint)" fi done else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 📁 No datasets (root pool only)" else echo " 📁 Sin datasets (solo pool raíz)" fi fi # Mostrar dispositivos del pool local pool_devices=$(zpool status "$pool" 2>/dev/null | grep -E "^\s+[a-z]" | awk '{print $1}' | grep -v "raidz\|mirror\|spare\|log\|cache\|replacing" | head -3) if [ -n "$pool_devices" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 💿 Devices: $(echo $pool_devices | tr '\n' ' ' | sed 's/ *$//')..." else echo " 💿 Dispositivos: $(echo $pool_devices | tr '\n' ' ' | sed 's/ *$//')..." fi fi echo "" done fi fi # 2. DETECTAR BTRFS if command -v btrfs &> /dev/null; then local btrfs_devices=() local btrfs_info=() # Buscar en todos los dispositivos for device in $(lsblk -dpno NAME | grep -E "sd[a-z]|nvme"); do if btrfs filesystem show "$device" 2>/dev/null | grep -q "uuid:"; then local device_name=$(basename "$device") local btrfs_uuid=$(btrfs filesystem show "$device" 2>/dev/null | grep "uuid:" | awk '{print $4}') local size=$(lsblk -dpno SIZE "$device" | tr -d ' ') local mount_point=$(mount | grep "$device" | awk '{print $3}' | head -1) btrfs_devices+=("$device_name") if [ -n "$mount_point" ]; then btrfs_info+=("$device_name:$size:$mount_point:$btrfs_uuid") else if [ "$SCRIPT_LANGUAGE" = "en" ]; then btrfs_info+=("$device_name:$size:not_mounted:$btrfs_uuid") else btrfs_info+=("$device_name:$size:no_montado:$btrfs_uuid") fi fi fi done if [ ${#btrfs_devices[@]} -gt 0 ]; then btrfs_found=true any_raid_found=true echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "🟠 DETECTED BTRFS FILESYSTEMS:" else echo "🟠 FILESYSTEMS BTRFS DETECTADOS:" fi for info in "${btrfs_info[@]}"; do IFS=':' read -r device size mount_point uuid <<< "$info" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 📦 Device: $device" echo " 📏 Size: $size" echo " 📁 Mount: $mount_point" echo " 🆔 UUID: $uuid" else echo " 📦 Dispositivo: $device" echo " 📏 Tamaño: $size" echo " 📁 Montaje: $mount_point" echo " 🆔 UUID: $uuid" fi # Mostrar información adicional de BTRFS si está montado if [ "$mount_point" != "no_montado" ] && [ "$mount_point" != "not_mounted" ]; then local used=$(df -h "$mount_point" 2>/dev/null | tail -1 | awk '{print $3}') local available=$(df -h "$mount_point" 2>/dev/null | tail -1 | awk '{print $4}') if [ -n "$used" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 📊 Used: $used, Available: $available" else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 📊 Used: $used, Available: $available" else echo " 📊 Usado: $used, Disponible: $available" fi fi fi # Verificar subvolúmenes local subvolumes=$(btrfs subvolume list "$mount_point" 2>/dev/null | wc -l) if [ "$subvolumes" -gt 0 ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 📂 Subvolumes: $subvolumes" else echo " 📂 Subvolúmenes: $subvolumes" fi fi fi echo "" done fi fi # 3. DETECTAR MDADM (Linux Software RAID) if command -v mdadm &> /dev/null; then local mdadm_arrays=$(cat /proc/mdstat 2>/dev/null | grep "^md" | awk '{print $1}') if [ -n "$mdadm_arrays" ]; then mdadm_found=true any_raid_found=true echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "🔴 DETECTED MDADM ARRAYS:" else echo "🔴 ARRAYS MDADM DETECTADOS:" fi for array in $mdadm_arrays; do local array_info=$(mdadm --detail "/dev/$array" 2>/dev/null) if [ -n "$array_info" ]; then local raid_level=$(echo "$array_info" | grep "Raid Level" | awk '{print $4}') local array_size=$(echo "$array_info" | grep "Array Size" | awk '{print $4$5}') local state=$(echo "$array_info" | grep "State" | awk '{print $3}') local num_devices=$(echo "$array_info" | grep "Total Devices" | awk '{print $4}') echo " 📦 Array: /dev/$array" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 🔧 RAID Level: $raid_level" echo " 📏 Size: $array_size" echo " 💚 Status: $state" echo " 💿 Devices: $num_devices" else echo " 🔧 Nivel RAID: $raid_level" echo " 📏 Tamaño: $array_size" echo " 💚 Estado: $state" echo " 💿 Dispositivos: $num_devices" fi # Mostrar dispositivos del array local devices=$(echo "$array_info" | grep "/dev/" | grep -v "failed\|spare" | awk '{print $7}' | head -3) if [ -n "$devices" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 💿 Members: $(echo $devices | tr '\n' ' ' | sed 's/ *$//')..." else echo " 💿 Miembros: $(echo $devices | tr '\n' ' ' | sed 's/ *$//')..." fi fi echo "" fi done fi fi # 4. DETECTAR LVM if command -v vgdisplay &> /dev/null; then local volume_groups=$(vgdisplay 2>/dev/null | grep "VG Name" | awk '{print $3}') if [ -n "$volume_groups" ]; then lvm_found=true any_raid_found=true echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "🟣 DETECTED LVM VOLUME GROUPS:" else echo "🟣 VOLUME GROUPS LVM DETECTADOS:" fi for vg in $volume_groups; do local vg_info=$(vgdisplay "$vg" 2>/dev/null) if [ -n "$vg_info" ]; then local vg_size=$(echo "$vg_info" | grep "VG Size" | awk '{print $3$4}') local vg_free=$(echo "$vg_info" | grep "Free PE" | awk '{print $5$6}') local pv_count=$(echo "$vg_info" | grep "Cur PV" | awk '{print $3}') local lv_count=$(echo "$vg_info" | grep "Cur LV" | awk '{print $3}') echo " 📦 Volume Group: $vg" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 📏 Size: $vg_size" echo " 💾 Free: $vg_free" echo " 💿 Physical Volumes: $pv_count" echo " 📁 Logical Volumes: $lv_count" else echo " 📏 Tamaño: $vg_size" echo " 💾 Libre: $vg_free" echo " 💿 Physical Volumes: $pv_count" echo " 📁 Logical Volumes: $lv_count" fi # Mostrar logical volumes local logical_volumes=$(lvdisplay "$vg" 2>/dev/null | grep "LV Name" | awk '{print $3}' | head -3) if [ -n "$logical_volumes" ]; then echo " 📂 LVs: $(echo $logical_volumes | tr '\n' ' ' | sed 's/ *$//')..." fi echo "" fi done fi fi # SHOW SUMMARY AND OPTIONS / MOSTRAR RESUMEN Y OPCIONES if [ "$any_raid_found" = true ]; then echo "═══════════════════════════════════════════════════════════════════" echo "" echo "$(get_text "available_options")" local option_number=1 if [ "$zfs_found" = true ]; then echo " $option_number. $(get_text "manage_zfs_pools")" zfs_option=$option_number ((option_number++)) echo " $option_number. $(get_text "delete_zfs_pools")" zfs_delete_option=$option_number ((option_number++)) fi if [ "$btrfs_found" = true ]; then echo " $option_number. $(get_text "delete_btrfs_filesystems")" btrfs_option=$option_number ((option_number++)) fi if [ "$mdadm_found" = true ]; then echo " $option_number. $(get_text "manage_mdadm_arrays")" mdadm_option=$option_number ((option_number++)) fi if [ "$lvm_found" = true ]; then echo " $option_number. $(get_text "manage_lvm_groups")" lvm_option=$option_number ((option_number++)) fi echo " $option_number. $(get_text "continuing_new_raid")" continue_option=$option_number ((option_number++)) echo " $option_number. $(get_text "exiting_script")" exit_option=$option_number echo "" while true; do read -p "👉 $(get_text "select_option") (1-$exit_option): " choice if [ "$zfs_found" = true ] && [ "$choice" = "$zfs_option" ]; then manage_existing_pools_datasets return 1 # Indica que se gestionaron configuraciones existentes elif [ "$zfs_found" = true ] && [ "$choice" = "$zfs_delete_option" ]; then delete_existing_zfs_pools return 1 # Indica que se eliminaron pools elif [ "$btrfs_found" = true ] && [ "$choice" = "$btrfs_option" ]; then local btrfs_devices_array=() for info in "${btrfs_info[@]}"; do IFS=':' read -r device size mount_point uuid <<< "$info" btrfs_devices_array+=("$device") done delete_existing_btrfs "${btrfs_devices_array[@]}" return 1 # Indica que se eliminaron filesystems elif [ "$mdadm_found" = true ] && [ "$choice" = "$mdadm_option" ]; then show_mdadm_details echo "" echo "$(get_text "mdadm_management_info")" echo " • mdadm --detail /dev/mdX" echo " • mdadm --stop /dev/mdX" echo " • mdadm --manage /dev/mdX --fail /dev/sdX" echo "" if confirm "$(get_text "continue_detection")"; then detect_existing_raid_configurations return $? else return 1 fi elif [ "$lvm_found" = true ] && [ "$choice" = "$lvm_option" ]; then show_lvm_details echo "" echo "$(get_text "lvm_management_info")" echo " • lvdisplay, vgdisplay, pvdisplay" echo " • lvextend, lvreduce" echo " • vgextend, vgreduce" echo "" if confirm "$(get_text "continue_detection")"; then detect_existing_raid_configurations return $? else return 1 fi elif [ "$choice" = "$continue_option" ]; then show_message "$(get_text "continuing_new_raid")..." return 0 # Continúa con el flujo normal elif [ "$choice" = "$exit_option" ]; then show_message "$(get_text "exiting_script")..." exit 0 else echo "❌ $(get_text "invalid_option")." fi done else echo "" echo "$(get_text "no_raid_detected")" echo "$(get_text "system_ready_new_raid")" echo "" return 0 # Continue with normal configuration / Continúa con configuración normal fi } # Función para mostrar detalles de arrays MDADM show_mdadm_details() { if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "MDADM Array Details" else show_title "Detalles de Arrays MDADM" fi local mdadm_arrays=$(cat /proc/mdstat 2>/dev/null | grep "^md" | awk '{print $1}') if [ -z "$mdadm_arrays" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "No MDADM arrays found" else show_warning "No se encontraron arrays MDADM" fi return 0 fi for array in $mdadm_arrays; do echo "" echo "📦 ARRAY: /dev/$array" echo "═══════════════════════════════════════════" # Información detallada del array local array_info=$(mdadm --detail "/dev/$array" 2>/dev/null) if [ -n "$array_info" ]; then echo "$array_info" else echo "❌ No se pudo obtener información detallada del array" fi echo "" echo "📊 ESTADO EN /proc/mdstat:" echo "═══════════════════════════════════════════" cat /proc/mdstat | grep -A 3 "^$array" echo "" done } # Función para mostrar detalles de Volume Groups LVM show_lvm_details() { if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "LVM Volume Group Details" else show_title "Detalles de Volume Groups LVM" fi local volume_groups=$(vgdisplay 2>/dev/null | grep "VG Name" | awk '{print $3}') if [ -z "$volume_groups" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "No LVM Volume Groups found" else show_warning "No se encontraron Volume Groups LVM" fi return 0 fi for vg in $volume_groups; do echo "" echo "📦 VOLUME GROUP: $vg" echo "═══════════════════════════════════════════" # Información detallada del VG vgdisplay "$vg" 2>/dev/null echo "" echo "📁 LOGICAL VOLUMES EN $vg:" echo "═══════════════════════════════════════════" lvdisplay "$vg" 2>/dev/null echo "" echo "💿 PHYSICAL VOLUMES EN $vg:" echo "═══════════════════════════════════════════" pvdisplay 2>/dev/null | grep -A 10 "$vg" | head -15 echo "" done } # Función para eliminar pools ZFS existentes delete_existing_zfs_pools() { if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "Delete Existing ZFS Pools" else show_title "Eliminación de Pools ZFS Existentes" fi local existing_pools=$(zpool list -H -o name 2>/dev/null) if [ -z "$existing_pools" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "No ZFS pools found for deletion" else show_warning "No se encontraron pools ZFS para eliminar" fi return 0 fi local pools_array=($existing_pools) echo "⚠️ ADVERTENCIA: Esta operación eliminará completamente los pools seleccionados" echo "🔥 TODOS LOS DATOS SE PERDERÁN PERMANENTEMENTE" echo "" echo "Pools ZFS disponibles para eliminar:" for i in "${!pools_array[@]}"; do local pool="${pools_array[$i]}" local pool_health=$(zpool list -H -o health "$pool" 2>/dev/null) local pool_size=$(zpool list -H -o size "$pool" 2>/dev/null) local pool_used=$(zpool list -H -o allocated "$pool" 2>/dev/null) echo " $((i+1)). $pool" echo " 💚 Estado: $pool_health | 📊 Tamaño: $pool_size | 📈 Usado: $pool_used" # Mostrar datasets local datasets=$(zfs list -H -o name -r "$pool" 2>/dev/null | grep -v "^${pool}$") if [ -n "$datasets" ]; then local dataset_count=$(echo "$datasets" | wc -l) echo " 📁 Datasets: $dataset_count" fi done echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Deletion options:" echo " • Pool number (e.g., 1, 2, 3)" echo " • Multiple pools separated by spaces (e.g., 1 3 4)" echo " • 'all' - Delete ALL pools (DANGEROUS!)" echo " • 'cancel' - Cancel operation" else echo "Opciones de eliminación:" echo " • Número del pool (ej: 1, 2, 3)" echo " • Múltiples pools separados por espacios (ej: 1 3 4)" echo " • 'all' - Eliminar TODOS los pools (¡PELIGROSO!)" echo " • 'cancel' - Cancelar operación" fi echo "" while true; do if [ "$SCRIPT_LANGUAGE" = "en" ]; then read -p "👉 Pools to delete: " choice else read -p "👉 Pools a eliminar: " choice fi if [ "$choice" = "cancel" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Operation cancelled by user" else show_message "Operación cancelada por el usuario" fi return 0 elif [ "$choice" = "all" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "⚠️ WARNING! You are going to delete ALL ZFS pools" show_warning "⚠️ This includes: ${pools_array[*]}" if confirm "Are you ABSOLUTELY SURE to delete ALL pools?"; then for pool in "${pools_array[@]}"; do echo "" destroy_zfs_pool_safely "$pool" done show_message "✅ All pools have been processed" return 0 else show_message "Mass deletion cancelled" continue fi else show_warning "⚠️ ¡ADVERTENCIA! Vas a eliminar TODOS los pools ZFS" show_warning "⚠️ Esto incluye: ${pools_array[*]}" if confirm "¿Estás ABSOLUTAMENTE SEGURO de eliminar TODOS los pools?"; then for pool in "${pools_array[@]}"; do echo "" destroy_zfs_pool_safely "$pool" done show_message "✅ Todos los pools han sido procesados" return 0 else show_message "Eliminación masiva cancelada" continue fi fi elif [[ "$choice" =~ ^[0-9\ ]+$ ]]; then # Validar selecciones local valid_selections=() local invalid_found=false for num in $choice; do if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le ${#pools_array[@]} ]; then local pool_index=$((num-1)) local pool_name="${pools_array[$pool_index]}" if [[ ! " ${valid_selections[*]} " =~ " ${pool_name} " ]]; then valid_selections+=("$pool_name") fi else echo "❌ Selección inválida: $num (usa números del 1 al ${#pools_array[@]})" invalid_found=true fi done if [ "$invalid_found" = true ]; then continue fi if [ ${#valid_selections[@]} -eq 0 ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "No valid pools selected" else show_error "No se seleccionaron pools válidos" fi continue fi # Mostrar resumen de pools a eliminar echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "Pools selected for deletion:" else show_warning "Pools seleccionados para eliminación:" fi for pool in "${valid_selections[@]}"; do local pool_size=$(zpool list -H -o size "$pool" 2>/dev/null) local pool_used=$(zpool list -H -o allocated "$pool" 2>/dev/null) echo " 🗑️ $pool (Tamaño: $pool_size, Usado: $pool_used)" done echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then if confirm "Do you confirm the deletion of these ${#valid_selections[@]} pools?"; then for pool in "${valid_selections[@]}"; do echo "" destroy_zfs_pool_safely "$pool" done show_message "✅ Selected pools have been processed" return 0 else show_message "Deletion cancelled" continue fi else if confirm "¿Confirmas la eliminación de estos ${#valid_selections[@]} pools?"; then for pool in "${valid_selections[@]}"; do echo "" destroy_zfs_pool_safely "$pool" done show_message "✅ Pools seleccionados han sido procesados" return 0 else show_message "Eliminación cancelada" continue fi fi else echo "❌ Opción inválida. Usa números, 'all' o 'cancel'" fi done } # Función para detectar y gestionar filesystems BTRFS existentes detect_and_manage_btrfs() { if ! command -v btrfs &> /dev/null; then return 0 fi # Buscar filesystems BTRFS existentes local btrfs_devices=() local btrfs_info=() # Buscar en todos los dispositivos for device in $(lsblk -dpno NAME | grep -E "sd[a-z]|nvme"); do if btrfs filesystem show "$device" 2>/dev/null | grep -q "uuid:"; then local device_name=$(basename "$device") local btrfs_uuid=$(btrfs filesystem show "$device" 2>/dev/null | grep "uuid:" | awk '{print $4}') local size=$(lsblk -dpno SIZE "$device" | tr -d ' ') local mount_point=$(mount | grep "$device" | awk '{print $3}' | head -1) btrfs_devices+=("$device_name") if [ -n "$mount_point" ]; then btrfs_info+=("$device_name:$size:$mount_point:$btrfs_uuid") else btrfs_info+=("$device_name:$size:no_montado:$btrfs_uuid") fi fi done if [ ${#btrfs_devices[@]} -gt 0 ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "Existing BTRFS Filesystems Detected" else show_title "Filesystems BTRFS Existentes Detectados" fi echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "The following BTRFS filesystems were found:" else echo "Se encontraron los siguientes filesystems BTRFS:" fi for info in "${btrfs_info[@]}"; do IFS=':' read -r device size mount_point uuid <<< "$info" echo " 📦 Dispositivo: $device" echo " 📏 Tamaño: $size" echo " 📁 Montaje: $mount_point" echo " 🆔 UUID: $uuid" echo "" done echo "🛠️ OPCIONES PARA BTRFS:" echo " 1. Eliminar filesystems BTRFS específicos" echo " 2. Continuar sin modificar BTRFS" echo "" while true; do read -p "👉 Selecciona una opción (1-2): " choice case $choice in 1) delete_existing_btrfs "${btrfs_devices[@]}" break ;; 2) if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Continuing without modifying BTRFS filesystems..." else show_message "Continuando sin modificar filesystems BTRFS..." fi break ;; *) if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "❌ Invalid option. Select 1 or 2." else echo "❌ Opción inválida. Selecciona 1 o 2." fi ;; esac done fi return 0 } # Función para eliminar filesystems BTRFS existentes delete_existing_btrfs() { local btrfs_devices=("$@") if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "Delete BTRFS Filesystems" else show_title "Eliminación de Filesystems BTRFS" fi echo "⚠️ ADVERTENCIA: Esta operación eliminará completamente los filesystems seleccionados" echo "🔥 TODOS LOS DATOS SE PERDERÁN PERMANENTEMENTE" echo "" echo "Filesystems BTRFS disponibles para eliminar:" for i in "${!btrfs_devices[@]}"; do local device="${btrfs_devices[$i]}" local size=$(lsblk -dpno SIZE "/dev/$device" | tr -d ' ') local mount_point=$(mount | grep "/dev/$device" | awk '{print $3}' | head -1) local uuid=$(btrfs filesystem show "/dev/$device" 2>/dev/null | grep "uuid:" | awk '{print $4}') echo " $((i+1)). $device" echo " 📏 Tamaño: $size" if [ -n "$mount_point" ]; then echo " 📁 Montado en: $mount_point" else echo " 📁 No montado" fi echo " 🆔 UUID: $uuid" done echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Deletion options:" echo " • Device number (e.g., 1, 2, 3)" echo " • Multiple devices separated by spaces (e.g., 1 3)" echo " • 'all' - Delete ALL BTRFS filesystems" echo " • 'cancel' - Cancel operation" else echo "Opciones de eliminación:" echo " • Número del dispositivo (ej: 1, 2, 3)" echo " • Múltiples dispositivos separados por espacios (ej: 1 3)" echo " • 'all' - Eliminar TODOS los filesystems BTRFS" echo " • 'cancel' - Cancelar operación" fi echo "" while true; do if [ "$SCRIPT_LANGUAGE" = "en" ]; then read -p "👉 Filesystems to delete: " choice else read -p "👉 Filesystems a eliminar: " choice fi if [ "$choice" = "cancel" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Operation cancelled by user" else show_message "Operación cancelada por el usuario" fi return 0 elif [ "$choice" = "all" ]; then show_warning "⚠️ ¡ADVERTENCIA! Vas a eliminar TODOS los filesystems BTRFS" show_warning "⚠️ Esto incluye: ${btrfs_devices[*]}" if confirm "¿Estás ABSOLUTAMENTE SEGURO de eliminar TODOS los filesystems BTRFS?"; then # Agrupar dispositivos por UUID para manejar arrays multi-disco local processed_uuids=() for device in "${btrfs_devices[@]}"; do local device_uuid=$(sudo btrfs filesystem show "/dev/$device" 2>/dev/null | grep "uuid:" | awk '{print $4}') # Verificar si ya procesamos este UUID if [[ ! " ${processed_uuids[*]} " =~ " ${device_uuid} " ]]; then processed_uuids+=("$device_uuid") # Obtener todos los dispositivos del array con este UUID local array_devices=$(sudo btrfs filesystem show "/dev/$device" 2>/dev/null | grep "devid" | awk '{print $NF}' | sed 's|/dev/||') echo "" show_warning "🗑️ Eliminando array BTRFS (UUID: $device_uuid)" show_message "📀 Dispositivos del array: $array_devices" # Usar el primer dispositivo para la eliminación (que limpiará todo el array) destroy_btrfs_array_safely "$device" "$device_uuid" fi done if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ All BTRFS filesystems have been processed" else show_message "✅ Todos los filesystems BTRFS han sido procesados" fi return 0 else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Mass deletion cancelled" else show_message "Eliminación masiva cancelada" fi continue fi elif [[ "$choice" =~ ^[0-9\ ]+$ ]]; then # Validar selecciones local valid_selections=() local invalid_found=false for num in $choice; do if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le ${#btrfs_devices[@]} ]; then local device_index=$((num-1)) local device_name="${btrfs_devices[$device_index]}" if [[ ! " ${valid_selections[*]} " =~ " ${device_name} " ]]; then valid_selections+=("$device_name") fi else echo "❌ Selección inválida: $num (usa números del 1 al ${#btrfs_devices[@]})" invalid_found=true fi done if [ "$invalid_found" = true ]; then continue fi if [ ${#valid_selections[@]} -eq 0 ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "No valid devices selected" else show_error "No se seleccionaron dispositivos válidos" fi continue fi # Mostrar resumen de dispositivos a eliminar echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "BTRFS filesystems selected for deletion:" else show_warning "Filesystems BTRFS seleccionados para eliminación:" fi for device in "${valid_selections[@]}"; do local size=$(lsblk -dpno SIZE "/dev/$device" | tr -d ' ') echo " 🗑️ $device (Tamaño: $size)" done echo "" if confirm "¿Confirmas la eliminación de estos ${#valid_selections[@]} filesystems?"; then # Agrupar dispositivos por UUID para manejar arrays multi-disco local processed_uuids=() for device in "${valid_selections[@]}"; do local device_uuid=$(sudo btrfs filesystem show "/dev/$device" 2>/dev/null | grep "uuid:" | awk '{print $4}') # Verificar si ya procesamos este UUID if [[ ! " ${processed_uuids[*]} " =~ " ${device_uuid} " ]]; then processed_uuids+=("$device_uuid") # Obtener información del array local btrfs_info=$(sudo btrfs filesystem show "/dev/$device" 2>/dev/null) local total_devices=$(echo "$btrfs_info" | grep "Total devices" | awk '{print $3}') echo "" if [ "$total_devices" -gt 1 ]; then # Es un array multi-disco, usar la función de array destroy_btrfs_array_safely "$device" "$device_uuid" else # Es un disco individual, usar la función tradicional destroy_btrfs_safely "$device" fi fi done if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ Selected BTRFS filesystems have been processed" else show_message "✅ Filesystems BTRFS seleccionados han sido procesados" fi return 0 else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Deletion cancelled" else show_message "Eliminación cancelada" fi continue fi else echo "❌ Opción inválida. Usa números, 'all' o 'cancel'" fi done } # Función para gestionar datasets de pools existentes manage_existing_pools_datasets() { if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "Dataset Management in Existing Pools" else show_title "Gestión de Datasets en Pools Existentes" fi local existing_pools=$(zpool list -H -o name 2>/dev/null) local pools_array=($existing_pools) if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Select the pool where you want to manage datasets:" else echo "Selecciona el pool donde quieres gestionar datasets:" fi for i in "${!pools_array[@]}"; do local pool="${pools_array[$i]}" local pool_health=$(zpool list -H -o health "$pool" 2>/dev/null) local pool_free=$(zpool list -H -o free "$pool" 2>/dev/null) if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " $((i+1)). $pool (Health: $pool_health, Free: $pool_free)" else echo " $((i+1)). $pool (Estado: $pool_health, Libre: $pool_free)" fi done echo "" while true; do if [ "$SCRIPT_LANGUAGE" = "en" ]; then read -p "👉 Select pool (1-${#pools_array[@]}): " pool_choice else read -p "👉 Selecciona pool (1-${#pools_array[@]}): " pool_choice fi if [[ "$pool_choice" =~ ^[0-9]+$ ]] && [ "$pool_choice" -ge 1 ] && [ "$pool_choice" -le ${#pools_array[@]} ]; then local selected_pool="${pools_array[$((pool_choice-1))]}" create_datasets_in_pool "$selected_pool" break else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "❌ Invalid selection. Use numbers from 1 to ${#pools_array[@]}." else echo "❌ Selección inválida. Usa números del 1 al ${#pools_array[@]}." fi fi done # Preguntar si quiere gestionar otro pool if [ ${#pools_array[@]} -gt 1 ]; then if confirm "¿Deseas gestionar datasets en otro pool?"; then manage_existing_pools_datasets fi fi } # Función para eliminar datasets de un pool específico delete_dataset_from_pool() { local pool_name="$1" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "Delete Datasets in Pool '$pool_name'" else show_title "Eliminación de Datasets en Pool '$pool_name'" fi # Obtener datasets existentes (excluyendo el pool raíz) local existing_datasets=$(zfs list -H -o name -r "$pool_name" 2>/dev/null | grep -v "^${pool_name}$") if [ -z "$existing_datasets" ]; then show_warning "No hay datasets para eliminar en el pool '$pool_name'" echo "Solo existe el pool raíz que no puede ser eliminado desde aquí." return 0 fi # Convertir a array para manejo más fácil local datasets_array=() while IFS= read -r dataset; do datasets_array+=("$dataset") done <<< "$existing_datasets" echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "⚠️ WARNING: Dataset deletion is PERMANENT" echo "🔥 ALL DATA from the selected dataset will be lost" else echo "⚠️ ADVERTENCIA: La eliminación de datasets es PERMANENTE" echo "🔥 TODOS LOS DATOS del dataset seleccionado se perderán" fi echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "📁 Datasets available for deletion:" else echo "📁 Datasets disponibles para eliminar:" fi for i in "${!datasets_array[@]}"; do local dataset="${datasets_array[$i]}" local used=$(zfs list -H -o used "$dataset" 2>/dev/null) local mountpoint=$(zfs list -H -o mountpoint "$dataset" 2>/dev/null) local compression=$(zfs get -H -o value compression "$dataset" 2>/dev/null) echo " $((i+1)). $dataset" echo " 📊 Usado: $used | 📁 Montaje: $mountpoint | 🗜️ Compresión: $compression" # Mostrar snapshots si existen local snapshots=$(zfs list -t snapshot -H -o name "$dataset" 2>/dev/null | wc -l) if [ "$snapshots" -gt 0 ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 📸 Snapshots: $snapshots (will also be deleted)" else echo " 📸 Snapshots: $snapshots (también serán eliminados)" fi fi echo "" done if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Deletion options:" echo " • Dataset number (e.g., 1, 2, 3)" echo " • Multiple datasets separated by spaces (e.g., 1 3 4)" echo " • 'all' - Delete ALL datasets (DANGEROUS!)" echo " • 'cancel' - Cancel operation" else echo "Opciones de eliminación:" echo " • Número del dataset (ej: 1, 2, 3)" echo " • Múltiples datasets separados por espacios (ej: 1 3 4)" echo " • 'all' - Eliminar TODOS los datasets (¡PELIGROSO!)" echo " • 'cancel' - Cancelar operación" fi echo "" while true; do if [ "$SCRIPT_LANGUAGE" = "en" ]; then read -p "👉 Datasets to delete: " choice else read -p "👉 Datasets a eliminar: " choice fi if [ "$choice" = "cancel" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Deletion cancelled by user" else show_message "Eliminación cancelada por el usuario" fi return 0 elif [ "$choice" = "all" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "⚠️ WARNING! You are going to delete ALL datasets" show_warning "⚠️ This includes: ${datasets_array[*]}" echo "" echo "🔥 THIS WILL PERMANENTLY DELETE:" for dataset in "${datasets_array[@]}"; do local used=$(zfs list -H -o used "$dataset" 2>/dev/null) echo " • $dataset (Used: $used)" done echo "" if confirm "Are you ABSOLUTELY SURE to delete ALL datasets?"; then # Eliminar en orden inverso para manejar dependencias for ((i=${#datasets_array[@]}-1; i>=0; i--)); do local dataset="${datasets_array[$i]}" echo "" delete_dataset_safely "$dataset" done show_message "✅ All datasets have been processed" return 0 else show_message "Mass deletion cancelled" continue fi else show_warning "⚠️ ¡ADVERTENCIA! Vas a eliminar TODOS los datasets" show_warning "⚠️ Esto incluye: ${datasets_array[*]}" echo "" echo "🔥 ESTO ELIMINARÁ PERMANENTEMENTE:" for dataset in "${datasets_array[@]}"; do local used=$(zfs list -H -o used "$dataset" 2>/dev/null) echo " • $dataset (Usado: $used)" done echo "" if confirm "¿Estás ABSOLUTAMENTE SEGURO de eliminar TODOS los datasets?"; then # Eliminar en orden inverso para manejar dependencias for ((i=${#datasets_array[@]}-1; i>=0; i--)); do local dataset="${datasets_array[$i]}" echo "" delete_dataset_safely "$dataset" done show_message "✅ Todos los datasets han sido procesados" return 0 else show_message "Eliminación masiva cancelada" continue fi fi elif [[ "$choice" =~ ^[0-9\ ]+$ ]]; then # Validar selecciones local valid_selections=() local invalid_found=false for num in $choice; do if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le ${#datasets_array[@]} ]; then local dataset_index=$((num-1)) local dataset_name="${datasets_array[$dataset_index]}" if [[ ! " ${valid_selections[*]} " =~ " ${dataset_name} " ]]; then valid_selections+=("$dataset_name") fi else echo "❌ Selección inválida: $num (usa números del 1 al ${#datasets_array[@]})" invalid_found=true fi done if [ "$invalid_found" = true ]; then continue fi if [ ${#valid_selections[@]} -eq 0 ]; then show_error "No se seleccionaron datasets válidos" continue fi # Mostrar resumen de datasets a eliminar echo "" show_warning "Datasets seleccionados para eliminación:" for dataset in "${valid_selections[@]}"; do local used=$(zfs list -H -o used "$dataset" 2>/dev/null) local snapshots=$(zfs list -t snapshot -H -o name "$dataset" 2>/dev/null | wc -l) echo " 🗑️ $dataset (Usado: $used)" if [ "$snapshots" -gt 0 ]; then echo " 📸 Incluye $snapshots snapshots" fi done echo "" if confirm "¿Confirmas la eliminación de estos ${#valid_selections[@]} datasets?"; then # Ordenar datasets para eliminar los más profundos primero # (importante para evitar errores de dependencias) local sorted_selections=($(printf '%s\n' "${valid_selections[@]}" | sort -r)) for dataset in "${sorted_selections[@]}"; do echo "" delete_dataset_safely "$dataset" done show_message "✅ Datasets seleccionados han sido procesados" return 0 else show_message "Eliminación cancelada" continue fi else echo "❌ Opción inválida. Usa números, 'all' o 'cancel'" fi done } # Función para eliminar un dataset de forma segura delete_dataset_safely() { local dataset="$1" if [ -z "$dataset" ]; then show_error "Nombre de dataset no especificado" return 1 fi # Verificar que el dataset existe if ! zfs list "$dataset" >/dev/null 2>&1; then show_warning "Dataset '$dataset' no encontrado" return 0 fi show_warning "🗑️ Eliminando dataset: '$dataset'" # Mostrar información del dataset antes de eliminar local used=$(zfs list -H -o used "$dataset" 2>/dev/null) local mountpoint=$(zfs list -H -o mountpoint "$dataset" 2>/dev/null) local compression=$(zfs get -H -o value compression "$dataset" 2>/dev/null) echo " 📊 Datos a eliminar: $used" echo " 📁 Punto de montaje: $mountpoint" echo " 🗜️ Compresión: $compression" # Verificar y mostrar snapshots local snapshots=$(zfs list -t snapshot -H -o name "$dataset" 2>/dev/null) if [ -n "$snapshots" ]; then local snapshot_count=$(echo "$snapshots" | wc -l) echo " 📸 Snapshots a eliminar: $snapshot_count" for snapshot in $snapshots; do local snap_used=$(zfs list -t snapshot -H -o used "$snapshot" 2>/dev/null) echo " • $snapshot (Usado: $snap_used)" done fi # Verificar si tiene datasets hijos local child_datasets=$(zfs list -H -o name -r "$dataset" 2>/dev/null | grep -v "^${dataset}$") if [ -n "$child_datasets" ]; then echo " 📂 Datasets hijos que también serán eliminados:" for child in $child_datasets; do local child_used=$(zfs list -H -o used "$child" 2>/dev/null) echo " • $child (Usado: $child_used)" done fi echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "⚠️ THIS ACTION WILL PERMANENTLY DELETE:" show_warning " • The dataset '$dataset' and all its data" if [ -n "$snapshots" ]; then show_warning " • All dataset snapshots" fi if [ -n "$child_datasets" ]; then show_warning " • All child datasets and their data" fi else show_warning "⚠️ ESTA ACCIÓN ELIMINARÁ PERMANENTEMENTE:" show_warning " • El dataset '$dataset' y todos sus datos" if [ -n "$snapshots" ]; then show_warning " • Todos los snapshots del dataset" fi if [ -n "$child_datasets" ]; then show_warning " • Todos los datasets hijos y sus datos" fi fi echo "" # Proceder con la eliminación if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "🔄 Deleting dataset '$dataset'..." else show_message "🔄 Eliminando dataset '$dataset'..." fi # 1. Desmontar el dataset si está montado if [ "$mountpoint" != "none" ] && [ "$mountpoint" != "-" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Unmounting dataset..." else show_message "Desmontando dataset..." fi sudo zfs unmount "$dataset" 2>/dev/null || true fi # 2. Eliminar el dataset con todos sus snapshots y descendientes if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Destroying dataset and dependencies..." else show_message "Destruyendo dataset y dependencias..." fi if sudo zfs destroy -r "$dataset" 2>/dev/null; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ Dataset '$dataset' deleted successfully" else show_message "✅ Dataset '$dataset' eliminado exitosamente" fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "⚠️ Standard attempt failed, trying forced deletion..." # Intentar eliminación forzada if sudo zfs destroy -f -r "$dataset" 2>/dev/null; then show_message "✅ Dataset '$dataset' forcibly deleted" else show_error "❌ Could not delete dataset '$dataset'" show_error " Possible causes:" show_error " • Dataset in use by some process" show_error " • Snapshot or clone dependencies" show_error " • Insufficient permissions" return 1 fi else show_warning "⚠️ Intento estándar falló, intentando eliminación forzada..." # Intentar eliminación forzada if sudo zfs destroy -f -r "$dataset" 2>/dev/null; then show_message "✅ Dataset '$dataset' eliminado forzadamente" else show_error "❌ No se pudo eliminar el dataset '$dataset'" show_error " Posibles causas:" show_error " • Dataset en uso por algún proceso" show_error " • Dependencias de snapshots o clones" show_error " • Permisos insuficientes" return 1 fi fi fi return 0 } # Función para crear datasets en un pool específico (reutilizable) create_datasets_in_pool() { local pool_name="$1" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "Dataset Management in Pool '$pool_name'" else show_title "Gestión de Datasets en Pool '$pool_name'" fi echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 DATASET INFORMATION:" else echo "💡 INFORMACIÓN SOBRE DATASETS:" fi echo " Los datasets son subdivisiones lógicas dentro del pool ZFS." echo " Beneficios:" echo " • Organización de datos (data, backups, media, etc.)" echo " • Cuotas de espacio independientes" echo " • Snapshots y backups granulares" echo " • Diferentes configuraciones de compresión" echo " • Puntos de montaje personalizados" echo "" echo " Ejemplo: $pool_name/data, $pool_name/backups, $pool_name/media" echo "" # Mostrar datasets existentes local existing_datasets=$(zfs list -H -o name -r "$pool_name" 2>/dev/null | grep -v "^${pool_name}$") if [ -n "$existing_datasets" ]; then echo "📁 Datasets existentes en '$pool_name':" for dataset in $existing_datasets; do local used=$(zfs list -H -o used "$dataset" 2>/dev/null) local mountpoint=$(zfs list -H -o mountpoint "$dataset" 2>/dev/null) local compression=$(zfs get -H -o value compression "$dataset" 2>/dev/null) echo " ✓ $dataset" echo " 📊 Usado: $used | 📁 Montaje: $mountpoint | 🗜️ Compresión: $compression" done echo "" fi echo "📋 DATASETS COMUNES SUGERIDOS:" echo " 1. data - Datos principales y documentos" echo " 2. media - Videos, fotos, música" echo " 3. backups - Copias de seguridad" echo " 4. temp - Archivos temporales" echo " 5. vm - Máquinas virtuales" echo " 6. docker - Contenedores Docker" echo " 7. homes - Directorios de usuario" echo "" local datasets_created=() while true; do echo "Opciones:" echo " • Nombre del dataset (ej: data, media, backups)" echo " • 'suggested' - Crear datasets comunes (data, media, backups)" echo " • 'list' - Mostrar datasets actuales" echo " • 'delete' - Eliminar dataset existente" echo " • 'done' - Finalizar gestión de datasets" echo "" read -p "👉 Dataset a crear/gestionar: " dataset_choice if [ "$dataset_choice" = "done" ]; then break elif [ "$dataset_choice" = "list" ]; then # Mostrar datasets actuales local current_datasets=$(zfs list -H -o name -r "$pool_name" 2>/dev/null | grep -v "^${pool_name}$") if [ -n "$current_datasets" ]; then echo "" echo "📁 Datasets actuales en '$pool_name':" for dataset in $current_datasets; do local used=$(zfs list -H -o used "$dataset" 2>/dev/null) local avail=$(zfs list -H -o avail "$dataset" 2>/dev/null) local mountpoint=$(zfs list -H -o mountpoint "$dataset" 2>/dev/null) if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " ✓ $dataset (Used: $used, Available: $avail, Mount: $mountpoint)" else echo " ✓ $dataset (Usado: $used, Disponible: $avail, Montaje: $mountpoint)" fi done echo "" else echo " (no hay datasets creados aún)" echo "" fi elif [ "$dataset_choice" = "delete" ]; then delete_dataset_from_pool "$pool_name" elif [ "$dataset_choice" = "suggested" ]; then local suggested_datasets=("data" "media" "backups") if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Creating suggested datasets: ${suggested_datasets[*]}" else show_message "Creando datasets sugeridos: ${suggested_datasets[*]}" fi for dataset in "${suggested_datasets[@]}"; do # Verificar si ya existe if zfs list "$pool_name/$dataset" >/dev/null 2>&1; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "ℹ️ Dataset '$pool_name/$dataset' already exists" else show_message "ℹ️ Dataset '$pool_name/$dataset' ya existe" fi continue fi if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Creating dataset: $pool_name/$dataset" else show_message "Creando dataset: $pool_name/$dataset" fi if sudo zfs create "$pool_name/$dataset"; then datasets_created+=("$dataset") if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ Dataset '$pool_name/$dataset' created successfully" else show_message "✅ Dataset '$pool_name/$dataset' creado exitosamente" fi # Configurar montaje automático sudo zfs set mountpoint="/$pool_name/$dataset" "$pool_name/$dataset" # Configuraciones específicas por tipo case $dataset in "media") # Optimizar para archivos grandes sudo zfs set recordsize=1M "$pool_name/$dataset" sudo zfs set compression=lz4 "$pool_name/$dataset" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message " 📺 Optimized for multimedia files" else show_message " 📺 Optimizado para archivos multimedia" fi ;; "backups") # Máxima compresión para backups sudo zfs set compression=gzip "$pool_name/$dataset" sudo zfs set dedup=on "$pool_name/$dataset" 2>/dev/null || true if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message " 💾 Optimized for backups (high compression)" else show_message " 💾 Optimizado para backups (alta compresión)" fi ;; "data") # Balance entre compresión y rendimiento sudo zfs set compression=lz4 "$pool_name/$dataset" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message " 📁 Optimized for general data" else show_message " 📁 Optimizado para datos generales" fi ;; esac else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "❌ Error creating dataset '$pool_name/$dataset'" else show_error "❌ Error creando dataset '$pool_name/$dataset'" fi fi done elif [ -n "$dataset_choice" ] && [[ "$dataset_choice" =~ ^[a-zA-Z0-9_-]+$ ]]; then # Crear dataset individual if zfs list "$pool_name/$dataset_choice" >/dev/null 2>&1; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "ℹ️ Dataset '$pool_name/$dataset_choice' already exists" else show_message "ℹ️ Dataset '$pool_name/$dataset_choice' ya existe" fi continue fi if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Creating dataset: $pool_name/$dataset_choice" else show_message "Creando dataset: $pool_name/$dataset_choice" fi if sudo zfs create "$pool_name/$dataset_choice"; then datasets_created+=("$dataset_choice") if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ Dataset '$pool_name/$dataset_choice' created successfully" else show_message "✅ Dataset '$pool_name/$dataset_choice' creado exitosamente" fi # Configurar montaje sudo zfs set mountpoint="/$pool_name/$dataset_choice" "$pool_name/$dataset_choice" # Preguntar por configuraciones específicas echo "" echo "⚙️ CONFIGURACIONES OPCIONALES para '$dataset_choice':" if confirm "¿Configurar compresión? (recomendado)"; then echo "Tipos de compresión:" echo "1. lz4 - Rápida, buen balance (recomendado)" echo "2. gzip - Alta compresión, más lenta" echo "3. off - Sin compresión" read -p "Selecciona (1-3) [1]: " compression_choice compression_choice=${compression_choice:-1} case $compression_choice in 1) sudo zfs set compression=lz4 "$pool_name/$dataset_choice";; 2) sudo zfs set compression=gzip "$pool_name/$dataset_choice";; 3) sudo zfs set compression=off "$pool_name/$dataset_choice";; esac if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message " 🗜️ Compression configured" else show_message " 🗜️ Compresión configurada" fi fi if [ "$SCRIPT_LANGUAGE" = "en" ]; then if confirm "Set space quota?"; then read -p "Quota in GB (e.g., 100): " quota_gb if [[ "$quota_gb" =~ ^[0-9]+$ ]] && [ "$quota_gb" -gt 0 ]; then sudo zfs set quota="${quota_gb}G" "$pool_name/$dataset_choice" show_message " 📏 Quota set: ${quota_gb}GB" fi fi else if confirm "¿Establecer cuota de espacio?"; then read -p "Cuota en GB (ej: 100): " quota_gb if [[ "$quota_gb" =~ ^[0-9]+$ ]] && [ "$quota_gb" -gt 0 ]; then sudo zfs set quota="${quota_gb}G" "$pool_name/$dataset_choice" show_message " 📏 Cuota establecida: ${quota_gb}GB" fi fi fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "❌ Error creating dataset '$pool_name/$dataset_choice'" else show_error "❌ Error creando dataset '$pool_name/$dataset_choice'" fi fi else if [ -z "$dataset_choice" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "❌ Dataset name cannot be empty" else show_error "❌ El nombre del dataset no puede estar vacío" fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "❌ Invalid name. Use only letters, numbers, hyphens and underscores" else show_error "❌ Nombre inválido. Usa solo letras, números, guiones y guiones bajos" fi fi fi echo "" done # Mostrar resumen de datasets creados en esta sesión if [ ${#datasets_created[@]} -gt 0 ]; then echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "📊 SUMMARY OF DATASETS CREATED IN THIS SESSION:" else show_message "📊 RESUMEN DE DATASETS CREADOS EN ESTA SESIÓN:" fi for dataset in "${datasets_created[@]}"; do local mountpoint=$(zfs get -H -o value mountpoint "$pool_name/$dataset" 2>/dev/null) local compression=$(zfs get -H -o value compression "$pool_name/$dataset" 2>/dev/null) local quota=$(zfs get -H -o value quota "$pool_name/$dataset" 2>/dev/null) echo " ✅ $pool_name/$dataset" echo " 📁 Montaje: $mountpoint" echo " 🗜️ Compresión: $compression" if [ "$quota" != "none" ]; then echo " 📏 Cuota: $quota" fi echo "" done fi } # Función para verificar si un disco está en uso por RAID check_disk_raid_status() { local disk="$1" local raid_info="" # Verificar BTRFS - Mejorado para detectar arrays multi-disco if command -v btrfs &> /dev/null; then # Primero verificar si el disco tiene un filesystem BTRFS if sudo btrfs filesystem show "/dev/$disk" 2>/dev/null | grep -q "uuid:"; then local btrfs_uuid=$(sudo btrfs filesystem show "/dev/$disk" 2>/dev/null | grep "uuid:" | awk '{print $4}') local btrfs_label=$(sudo btrfs filesystem show "/dev/$disk" 2>/dev/null | grep "Label:" | awk '{print $2}') # Obtener información detallada del array BTRFS local btrfs_info=$(sudo btrfs filesystem show "/dev/$disk" 2>/dev/null) local total_devices=$(echo "$btrfs_info" | grep "Total devices" | awk '{print $3}') local device_list=$(echo "$btrfs_info" | grep "devid" | awk '{print $NF}' | sed 's|/dev/||' | tr '\n' ',' | sed 's/,$//') # Obtener el punto de montaje si existe local mount_point=$(mount | grep "/dev/$disk" | awk '{print $3}' | head -1) # Intentar obtener el tipo de RAID si está montado local raid_profile="" if [ -n "$mount_point" ]; then raid_profile=$(sudo btrfs filesystem usage "$mount_point" 2>/dev/null | grep "Data," | head -1 | awk '{print $1}' | cut -d',' -f2 | cut -d':' -f1) if [ -z "$raid_profile" ]; then raid_profile="single" fi fi # Construir información del RAID if [ "$total_devices" -gt 1 ]; then if [ -n "$mount_point" ]; then raid_info="BTRFS RAID ($raid_profile, $total_devices discos: $device_list) - Montado en: $mount_point" else raid_info="BTRFS RAID ($total_devices discos: $device_list)" fi else if [ -n "$mount_point" ]; then raid_info="BTRFS (Single disk) - Montado en: $mount_point" else raid_info="BTRFS (Single disk)" fi fi fi fi # Verificar ZFS - método corregido con sed if command -v zpool &> /dev/null; then # Obtener lista de todos los pools local all_pools=$(zpool list -H -o name 2>/dev/null) for pool in $all_pools; do if [ -n "$pool" ]; then # Obtener dispositivos del pool usando sed (funciona mejor) local pool_devices=$(zpool status "$pool" 2>/dev/null | sed -n '/^\s*sd[a-z]/p' | awk '{print $1}') # Verificar si el disco está en la lista if echo "$pool_devices" | grep -q "^${disk}$"; then local pool_health=$(zpool list -H -o health "$pool" 2>/dev/null) local pool_size=$(zpool list -H -o size "$pool" 2>/dev/null) local pool_type=$(zpool status "$pool" 2>/dev/null | grep -o 'raidz[0-9]*-[0-9]*\|mirror-[0-9]*' | head -1) # Si no hay pool_type específico, es un stripe if [ -z "$pool_type" ]; then pool_type="stripe" fi raid_info="ZFS Pool: $pool ($pool_type, $pool_health, $pool_size)" break fi # También verificar otros tipos de dispositivos (nvme, etc.) if [ -z "$raid_info" ]; then local all_devices=$(zpool status "$pool" 2>/dev/null | sed -n '/^\s*[a-z]/p' | grep -v 'raidz\|mirror\|spare\|log\|cache\|replacing' | awk '{print $1}') if echo "$all_devices" | grep -q "^${disk}$"; then local pool_health=$(zpool list -H -o health "$pool" 2>/dev/null) local pool_size=$(zpool list -H -o size "$pool" 2>/dev/null) local pool_type=$(zpool status "$pool" 2>/dev/null | grep -o 'raidz[0-9]*-[0-9]*\|mirror-[0-9]*' | head -1) # Si no hay pool_type específico, es un stripe if [ -z "$pool_type" ]; then pool_type="stripe" fi raid_info="ZFS Pool: $pool ($pool_type, $pool_health, $pool_size)" break fi fi # También verificar particiones (sdc1, sdc9, etc.) local disk_partitions=$(lsblk -ln -o NAME "/dev/$disk" 2>/dev/null | grep -v "^${disk##*/}$") for partition in $disk_partitions; do if echo "$pool_devices $all_devices" | grep -q "^${partition}$"; then local pool_health=$(zpool list -H -o health "$pool" 2>/dev/null) local pool_size=$(zpool list -H -o size "$pool" 2>/dev/null) local pool_type=$(zpool status "$pool" 2>/dev/null | grep -o 'raidz[0-9]*-[0-9]*\|mirror-[0-9]*' | head -1) # Si no hay pool_type específico, es un stripe if [ -z "$pool_type" ]; then pool_type="stripe" fi raid_info="ZFS Pool: $pool ($pool_type, $pool_health, $pool_size)" break 2 fi done fi done fi # Verificar mdadm (Linux Software RAID) if command -v mdadm &> /dev/null; then local md_array=$(mdadm --examine "/dev/$disk" 2>/dev/null | grep "Array UUID" | awk '{print $4}') if [ -n "$md_array" ]; then local md_name=$(mdadm --examine "/dev/$disk" 2>/dev/null | grep "Name" | awk '{print $3}') raid_info="mdadm RAID: $md_name (UUID: $md_array)" fi fi # Verificar LVM if command -v pvdisplay &> /dev/null; then if pvdisplay "/dev/$disk" 2>/dev/null | grep -q "PV Name"; then local vg_name=$(pvdisplay "/dev/$disk" 2>/dev/null | grep "VG Name" | awk '{print $3}') raid_info="LVM Physical Volume (VG: $vg_name)" fi fi # Verificar si tiene particiones montadas (que no sean ZFS) if mount | grep -q "/dev/$disk" && [ -z "$(echo "$raid_info" | grep ZFS)" ]; then local mount_points=$(mount | grep "/dev/$disk" | awk '{print $3}' | tr '\n' ' ') if [ -n "$raid_info" ]; then raid_info="$raid_info, Montado en: $mount_points" else raid_info="Montado en: $mount_points" fi fi echo "$raid_info" } # Función para eliminar pools ZFS específicos de forma segura destroy_zfs_pool_safely() { local pool_name="$1" local force="${2:-false}" if [ -z "$pool_name" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "ZFS pool name not specified" else show_error "Nombre de pool ZFS no especificado" fi return 1 fi # Verificar que el pool existe if ! zpool list -H -o name 2>/dev/null | grep -q "^${pool_name}$"; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "ZFS pool '$pool_name' not found" else show_warning "Pool ZFS '$pool_name' no encontrado" fi return 0 fi if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "🗑️ Preparing deletion of ZFS pool: '$pool_name'" else show_warning "🗑️ Preparando eliminación del pool ZFS: '$pool_name'" fi # Mostrar información del pool antes de eliminar local pool_size=$(zpool list -H -o size "$pool_name" 2>/dev/null) local pool_health=$(zpool list -H -o health "$pool_name" 2>/dev/null) local pool_devices=$(zpool status "$pool_name" 2>/dev/null | grep -E "^\s+[a-z]" | awk '{print $1}' | grep -v "raidz\|mirror\|spare\|log\|cache\|replacing") echo " 📊 Tamaño: $pool_size" echo " 💚 Estado: $pool_health" echo " 💿 Dispositivos: $(echo $pool_devices | tr '\n' ' ')" # Mostrar datasets que se eliminarán local datasets=$(zfs list -H -o name -r "$pool_name" 2>/dev/null) if [ -n "$datasets" ]; then echo " 📁 Datasets que se eliminarán:" for dataset in $datasets; do local used=$(zfs list -H -o used "$dataset" 2>/dev/null) echo " • $dataset (Usado: $used)" done fi # Mostrar snapshots que se eliminarán local snapshots=$(zfs list -t snapshot -H -o name -r "$pool_name" 2>/dev/null) if [ -n "$snapshots" ]; then echo " 📸 Snapshots que se eliminarán:" for snapshot in $snapshots; do echo " • $snapshot" done fi echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "⚠️ THIS ACTION WILL PERMANENTLY DELETE:" show_warning " • The complete ZFS pool '$pool_name'" show_warning " • All datasets and their data" show_warning " • All snapshots" show_warning " • Automatic mount configuration" else show_warning "⚠️ ESTA ACCIÓN ELIMINARÁ PERMANENTEMENTE:" show_warning " • El pool ZFS '$pool_name' completo" show_warning " • Todos los datasets y sus datos" show_warning " • Todos los snapshots" show_warning " • Configuración de montaje automático" fi echo "" if [ "$force" != "true" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then if ! confirm "Are you COMPLETELY SURE to delete pool '$pool_name'?"; then show_message "Deletion cancelled by user" return 1 fi else if ! confirm "¿Estás COMPLETAMENTE SEGURO de eliminar el pool '$pool_name'?"; then show_message "Eliminación cancelada por el usuario" return 1 fi fi fi # Proceder con la eliminación if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "🔄 Deleting ZFS pool '$pool_name'..." else show_message "🔄 Eliminando pool ZFS '$pool_name'..." fi # 1. Desmontार todos los datasets if [ -n "$datasets" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Unmounting datasets..." else show_message "Desmontando datasets..." fi for dataset in $datasets; do sudo zfs unmount "$dataset" 2>/dev/null || true done fi # 2. Intentar exportar el pool primero if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Exporting pool..." else show_message "Exportando pool..." fi sudo zpool export "$pool_name" 2>/dev/null || true sleep 1 # 3. Destruir el pool if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Destroying pool..." else show_message "Destruyendo pool..." fi if sudo zpool destroy -f "$pool_name" 2>/dev/null; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ ZFS pool '$pool_name' deleted successfully" else show_message "✅ Pool ZFS '$pool_name' eliminado exitosamente" fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "⚠️ Standard attempt failed, forcing deletion..." else show_warning "⚠️ Intento estándar falló, forzando eliminación..." fi # Intentar importar y destruir forzadamente sudo zpool import "$pool_name" 2>/dev/null || true sleep 1 if sudo zpool destroy -f "$pool_name" 2>/dev/null; then show_message "✅ Pool ZFS '$pool_name' eliminado forzadamente" else show_error "❌ No se pudo eliminar el pool '$pool_name'" return 1 fi fi # 4. Limpiar referencias en servicios de sistema if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Cleaning system configuration..." else show_message "Limpiando configuración del sistema..." fi # Limpiar cache de zpool sudo rm -f /etc/zfs/zpool.cache 2>/dev/null || true # Regenerar cache sudo zpool import -a 2>/dev/null || true return 0 } # Función para eliminar filesystem BTRFS de forma segura destroy_btrfs_safely() { local disk="$1" local force="${2:-false}" if [ -z "$disk" ]; then show_error "Disco no especificado para eliminación BTRFS" return 1 fi # Verificar que es BTRFS if ! btrfs filesystem show "/dev/$disk" 2>/dev/null | grep -q "uuid:"; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "No BTRFS filesystem found on /dev/$disk" else show_warning "No se encontró filesystem BTRFS en /dev/$disk" fi return 0 fi if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "🗑️ Preparing deletion of BTRFS filesystem on: /dev/$disk" else show_warning "🗑️ Preparando eliminación del filesystem BTRFS en: /dev/$disk" fi # Obtener información del filesystem local btrfs_uuid=$(btrfs filesystem show "/dev/$disk" 2>/dev/null | grep "uuid:" | awk '{print $4}') local btrfs_size=$(lsblk -dpno SIZE "/dev/$disk" | tr -d ' ') local mount_point=$(mount | grep "/dev/$disk" | awk '{print $3}' | head -1) echo " 📊 Tamaño: $btrfs_size" echo " 🆔 UUID: $btrfs_uuid" if [ -n "$mount_point" ]; then echo " 📁 Montado en: $mount_point" fi # Mostrar subvolúmenes si existen if [ -n "$mount_point" ]; then local subvolumes=$(btrfs subvolume list "$mount_point" 2>/dev/null | awk '{print $9}') if [ -n "$subvolumes" ]; then echo " 📂 Subvolúmenes que se eliminarán:" for subvol in $subvolumes; do echo " • $subvol" done fi fi echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "⚠️ THIS ACTION WILL PERMANENTLY DELETE:" show_warning " • The complete BTRFS filesystem on /dev/$disk" show_warning " • All data and subvolumes" show_warning " • Automatic mount entries in /etc/fstab" else show_warning "⚠️ ESTA ACCIÓN ELIMINARÁ PERMANENTEMENTE:" show_warning " • El filesystem BTRFS completo en /dev/$disk" show_warning " • Todos los datos y subvolúmenes" show_warning " • Entradas de montaje automático en /etc/fstab" fi echo "" if [ "$force" != "true" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then if ! confirm "Are you COMPLETELY SURE to delete BTRFS on /dev/$disk?"; then show_message "Deletion cancelled by user" return 1 fi else if ! confirm "¿Estás COMPLETAMENTE SEGURO de eliminar el BTRFS en /dev/$disk?"; then show_message "Eliminación cancelada por el usuario" return 1 fi fi fi # Proceder con la eliminación if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "🔄 Deleting BTRFS filesystem on /dev/$disk..." else show_message "🔄 Eliminando filesystem BTRFS en /dev/$disk..." fi # 1. Desmontar el filesystem if [ -n "$mount_point" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Unmounting $mount_point..." else show_message "Desmontando $mount_point..." fi sudo umount "$mount_point" 2>/dev/null || sudo umount -f "$mount_point" 2>/dev/null || true fi # Desmontar cualquier partición del disco sudo umount "/dev/$disk"* 2>/dev/null || true # 2. Eliminar entradas de /etc/fstab específicas de BTRFS de forma segura if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Removing BTRFS entries from /etc/fstab..." else show_message "Eliminando entradas BTRFS de /etc/fstab..." fi if [ -n "$btrfs_uuid" ]; then # Crear backup de fstab con timestamp local backup_file="/etc/fstab.backup.$(date +%Y%m%d-%H%M%S)" sudo cp /etc/fstab "$backup_file" 2>/dev/null || true if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "📋 fstab backup created: $backup_file" else show_message "📋 Backup de fstab creado: $backup_file" fi # Verificar qué entradas existen antes de eliminar local existing_entries=$(grep -E "UUID=$btrfs_uuid" /etc/fstab 2>/dev/null | grep -i btrfs || true) if [ -n "$existing_entries" ]; then show_message "📝 Entradas BTRFS encontradas en /etc/fstab:" echo "$existing_entries" | while read -r entry; do echo " • $entry" done # Eliminar entradas por UUID solo si el tipo de filesystem es btrfs # Usar patrones más específicos para mayor seguridad sudo sed -i "/^[[:space:]]*UUID=$btrfs_uuid[[:space:]].*[[:space:]]btrfs[[:space:]]/d" /etc/fstab 2>/dev/null || true sudo sed -i "/^[[:space:]]*UUID=$btrfs_uuid[[:space:]].*[[:space:]]btrfs$/d" /etc/fstab 2>/dev/null || true # Verificar si las entradas fueron eliminadas local remaining_entries=$(grep -E "UUID=$btrfs_uuid" /etc/fstab 2>/dev/null | grep -i btrfs || true) if [ -z "$remaining_entries" ]; then show_message "✅ Entradas BTRFS eliminadas exitosamente de /etc/fstab" else show_warning "⚠️ Algunas entradas BTRFS permanecen en /etc/fstab:" echo "$remaining_entries" | while read -r entry; do echo " • $entry" done fi else show_message "ℹ️ No se encontraron entradas BTRFS automount en /etc/fstab para UUID: $btrfs_uuid" fi fi # También verificar entradas por device path (menos común pero posible) local device_entries=$(grep -E "/dev/$disk" /etc/fstab 2>/dev/null | grep -i btrfs || true) if [ -n "$device_entries" ]; then show_message "📝 Entradas por device path encontradas:" echo "$device_entries" | while read -r entry; do echo " • $entry" done # Eliminar por device path pero solo líneas que contengan btrfs sudo sed -i "\|^[[:space:]]*/dev/$disk[[:space:]].*[[:space:]]btrfs[[:space:]]|d" /etc/fstab 2>/dev/null || true sudo sed -i "\|^[[:space:]]*/dev/$disk[0-9]*[[:space:]].*[[:space:]]btrfs[[:space:]]|d" /etc/fstab 2>/dev/null || true # Verificar si fueron eliminadas local remaining_device_entries=$(grep -E "/dev/$disk" /etc/fstab 2>/dev/null | grep -i btrfs || true) if [ -z "$remaining_device_entries" ]; then show_message "✅ Entradas por device path eliminadas exitosamente" fi fi # 3. Limpiar metadatos BTRFS if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Cleaning BTRFS metadata..." else show_message "Limpiando metadatos BTRFS..." fi sudo wipefs -af "/dev/$disk" 2>/dev/null || true if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ BTRFS filesystem on /dev/$disk deleted successfully" else show_message "✅ Filesystem BTRFS en /dev/$disk eliminado exitosamente" fi return 0 } # Función para eliminar array BTRFS completo de forma segura destroy_btrfs_array_safely() { local primary_disk="$1" local array_uuid="$2" if [ -z "$primary_disk" ] || [ -z "$array_uuid" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "Incorrect parameters for BTRFS array deletion" else show_error "Parámetros incorrectos para eliminación de array BTRFS" fi return 1 fi if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "🗑️ Deleting complete BTRFS array (UUID: $array_uuid)" else show_warning "🗑️ Eliminando array BTRFS completo (UUID: $array_uuid)" fi # Obtener información completa del array local btrfs_info=$(sudo btrfs filesystem show "/dev/$primary_disk" 2>/dev/null) local total_devices=$(echo "$btrfs_info" | grep "Total devices" | awk '{print $3}') local all_devices=$(echo "$btrfs_info" | grep "devid" | awk '{print $NF}' | sed 's|/dev/||') local mount_point=$(mount | grep "UUID=$array_uuid" | awk '{print $3}' | head -1) echo " 📊 Total de dispositivos: $total_devices" echo " 📀 Dispositivos: $all_devices" if [ -n "$mount_point" ]; then echo " 📁 Montado en: $mount_point" fi # Obtener tipo de RAID si está montado local raid_profile="" if [ -n "$mount_point" ]; then raid_profile=$(sudo btrfs filesystem usage "$mount_point" 2>/dev/null | grep "Data," | head -1 | awk '{print $1}' | cut -d',' -f2 | cut -d':' -f1) if [ -n "$raid_profile" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 🔧 RAID Configuration: $raid_profile" else echo " 🔧 Configuración RAID: $raid_profile" fi fi fi echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "⚠️ THIS ACTION WILL PERMANENTLY DELETE:" show_warning " • The entire BTRFS array ($total_devices devices)" show_warning " • All data and subvolumes" show_warning " • Automatic mount entries in /etc/fstab" else show_warning "⚠️ ESTA ACCIÓN ELIMINARÁ PERMANENTEMENTE:" show_warning " • Todo el array BTRFS ($total_devices dispositivos)" show_warning " • Todos los datos y subvolúmenes" show_warning " • Entradas de montaje automático en /etc/fstab" fi echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then if ! confirm "Are you COMPLETELY SURE to delete this complete BTRFS array?"; then show_message "Deletion cancelled by user" return 1 fi else if ! confirm "¿Estás COMPLETAMENTE SEGURO de eliminar este array BTRFS completo?"; then show_message "Eliminación cancelada por el usuario" return 1 fi fi # Proceder con la eliminación del array completo show_message "🔄 Eliminando array BTRFS..." # 1. Desmontar el filesystem if [ -n "$mount_point" ]; then show_message "Desmontando $mount_point..." sudo umount "$mount_point" 2>/dev/null || sudo umount -f "$mount_point" 2>/dev/null || true fi # Desmontar todos los dispositivos del array for device in $all_devices; do sudo umount "/dev/$device"* 2>/dev/null || true done # 2. Eliminar entradas de /etc/fstab para el array completo show_message "Eliminando entradas del array BTRFS de /etc/fstab..." local backup_file="/etc/fstab.backup.$(date +%Y%m%d-%H%M%S)" sudo cp /etc/fstab "$backup_file" 2>/dev/null || true if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "📋 fstab backup created: $backup_file" else show_message "📋 Backup de fstab creado: $backup_file" fi # Verificar entradas existentes local existing_entries=$(grep -E "UUID=$array_uuid" /etc/fstab 2>/dev/null | grep -i btrfs || true) if [ -n "$existing_entries" ]; then show_message "📝 Eliminando entradas del array BTRFS:" echo "$existing_entries" | while read -r entry; do echo " • $entry" done # Eliminar entradas por UUID del array sudo sed -i "/^[[:space:]]*UUID=$array_uuid[[:space:]].*[[:space:]]btrfs[[:space:]]/d" /etc/fstab 2>/dev/null || true sudo sed -i "/^[[:space:]]*UUID=$array_uuid[[:space:]].*[[:space:]]btrfs$/d" /etc/fstab 2>/dev/null || true # Verificar eliminación local remaining_entries=$(grep -E "UUID=$array_uuid" /etc/fstab 2>/dev/null | grep -i btrfs || true) if [ -z "$remaining_entries" ]; then show_message "✅ Entradas del array eliminadas exitosamente de /etc/fstab" else show_warning "⚠️ Algunas entradas del array permanecen en /etc/fstab" fi else show_message "ℹ️ No se encontraron entradas automount para este array" fi # 3. Limpiar metadatos BTRFS de todos los dispositivos if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Cleaning BTRFS metadata from all devices..." else show_message "Limpiando metadatos BTRFS de todos los dispositivos..." fi for device in $all_devices; do if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message " Cleaning /dev/$device..." else show_message " Limpiando /dev/$device..." fi sudo wipefs -af "/dev/$device" 2>/dev/null || true done if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ Complete BTRFS array deleted successfully" show_message " 📀 Devices cleaned: $all_devices" else show_message "✅ Array BTRFS completo eliminado exitosamente" show_message " 📀 Dispositivos limpiados: $all_devices" fi return 0 } # Función para limpiar disco de configuraciones RAID anteriores clean_disk() { local disk="$1" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Cleaning disk /dev/$disk..." else show_message "Limpiando disco /dev/$disk..." fi # Desmontar si está montado if mount | grep -q "/dev/$disk"; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Unmounting /dev/$disk..." else show_message "Desmontando /dev/$disk..." fi sudo umount "/dev/$disk"* 2>/dev/null || true fi # Verificar y destruir pools ZFS que usen este disco if command -v zpool &> /dev/null; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Checking ZFS pools using /dev/$disk..." else show_message "Verificando pools ZFS que usan /dev/$disk..." fi # Obtener lista de todos los pools local all_pools=$(zpool list -H -o name 2>/dev/null) local pools_to_destroy=() for pool in $all_pools; do if [ -n "$pool" ]; then # Verificar si este disco está en el pool if zpool status "$pool" 2>/dev/null | sed -n '/^\s*sd[a-z]/p' | awk '{print $1}' | grep -q "^${disk}$"; then pools_to_destroy+=("$pool") elif zpool status "$pool" 2>/dev/null | sed -n '/^\s*[a-z]/p' | grep -v 'raidz\|mirror\|spare\|log\|cache\|replacing' | awk '{print $1}' | grep -q "^${disk}$"; then pools_to_destroy+=("$pool") fi # También verificar particiones local disk_partitions=$(lsblk -ln -o NAME "/dev/$disk" 2>/dev/null | grep -v "^${disk##*/}$") for partition in $disk_partitions; do if zpool status "$pool" 2>/dev/null | grep -q "$partition"; then pools_to_destroy+=("$pool") break fi done fi done # Eliminar duplicados de pools local unique_pools=($(printf "%s\n" "${pools_to_destroy[@]}" | sort -u)) # Destruir pools encontrados usando la función segura for pool in "${unique_pools[@]}"; do if [ -n "$pool" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "🗑️ Detected ZFS pool '$pool' using disk /dev/$disk" else show_warning "🗑️ Detectado pool ZFS '$pool' usando el disco /dev/$disk" fi # Usar la función de eliminación segura if ! destroy_zfs_pool_safely "$pool" "true"; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "❌ Error deleting pool '$pool'" show_message "Continuing with manual cleanup..." show_warning "⚠️ Trying emergency method..." else show_error "❌ Error eliminando el pool '$pool'" show_message "Continuando con limpieza manual..." show_warning "⚠️ Intentando método de emergencia..." fi # Fallback a método anterior si la función segura falla sudo zpool export "$pool" 2>/dev/null || true sleep 1 sudo zpool destroy -f "$pool" 2>/dev/null || true fi fi done # Limpiar etiquetas ZFS residuales en el disco y particiones sudo zpool labelclear -f "/dev/$disk" 2>/dev/null || true # Limpiar particiones ZFS si existen local disk_partitions=$(lsblk -ln -o NAME "/dev/$disk" 2>/dev/null | grep -v "^${disk##*/}$") for partition in $disk_partitions; do local full_partition="/dev/${partition}" sudo zpool labelclear -f "$full_partition" 2>/dev/null || true done fi # Verificar y limpiar BTRFS usando la función segura if command -v btrfs &> /dev/null; then if btrfs filesystem show "/dev/$disk" 2>/dev/null | grep -q "uuid:"; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "🗑️ Detected BTRFS filesystem on /dev/$disk" else show_warning "🗑️ Detectado filesystem BTRFS en /dev/$disk" fi # Usar la función de eliminación segura para BTRFS if ! destroy_btrfs_safely "$disk" "true"; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "❌ Error deleting BTRFS filesystem" show_message "Continuing with manual cleanup..." else show_error "❌ Error eliminando filesystem BTRFS" show_message "Continuando con limpieza manual..." fi # Fallback a limpieza básica sudo wipefs -a "/dev/$disk" 2>/dev/null || true fi fi fi # Limpiar metadatos mdadm if command -v mdadm &> /dev/null; then sudo mdadm --zero-superblock "/dev/$disk" 2>/dev/null || true fi # Limpiar metadatos LVM if command -v pvremove &> /dev/null; then sudo pvremove -ff "/dev/$disk" 2>/dev/null || true fi # Limpiar tabla de particiones y metadatos completamente if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "🧹 Cleaning partition table and metadata from /dev/$disk..." else show_message "🧹 Limpiando tabla de particiones y metadatos de /dev/$disk..." fi # Limpiar los primeros 100MB y los últimos 100MB del disco sudo dd if=/dev/zero of="/dev/$disk" bs=1M count=100 2>/dev/null || true # Obtener el tamaño del disco y limpiar el final también local disk_size=$(lsblk -dpno SIZE "/dev/$disk" --bytes 2>/dev/null || echo "0") if [ "$disk_size" -gt 104857600 ]; then # Si es mayor a 100MB local seek_pos=$(( (disk_size / 1048576) - 100 )) # 100MB antes del final sudo dd if=/dev/zero of="/dev/$disk" bs=1M seek="$seek_pos" count=100 2>/dev/null || true fi # Limpiar con wipefs para asegurar que no queden metadatos sudo wipefs -af "/dev/$disk" 2>/dev/null || true # Forzar actualización de la tabla de particiones sudo partprobe "/dev/$disk" 2>/dev/null || true sudo udevadm settle 2>/dev/null || true # Esperar un poco más para que el sistema reconozca los cambios sleep 3 if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ Disk /dev/$disk completely cleaned" else show_message "✅ Disco /dev/$disk limpiado completamente" fi } # Función para detectar discos disponibles detect_disks() { if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "Detecting available disks" else show_title "Detectando discos disponibles" fi # Obtener lista de discos, excluyendo dispositivos del sistema AVAILABLE_DISKS=() DISK_RAID_STATUS=() # Obtener disco raíz ROOT_DISK=$(lsblk -no PKNAME $(findmnt -n -o SOURCE /)) # Obtener discos con particiones montadas en puntos críticos del sistema SYSTEM_DISKS=() SYSTEM_DISKS+=("$ROOT_DISK") # Detectar discos que contienen particiones del sistema while IFS= read -r device mount_point; do # Excluir puntos de montaje críticos del sistema if [[ "$mount_point" == "/" ]] || [[ "$mount_point" == "/boot"* ]] || \ [[ "$mount_point" == "/usr"* ]] || [[ "$mount_point" == "/var"* ]] || \ [[ "$mount_point" == "/etc"* ]] || [[ "$mount_point" == "/lib"* ]] || \ [[ "$mount_point" == "/bin"* ]] || [[ "$mount_point" == "/sbin"* ]]; then # Extraer el disco base del device local base_disk=$(lsblk -no PKNAME "$device" 2>/dev/null || echo "$device" | sed 's|/dev/||' | sed 's/[0-9]*$//') if [ -n "$base_disk" ] && [[ ! " ${SYSTEM_DISKS[*]} " =~ " ${base_disk} " ]]; then SYSTEM_DISKS+=("$base_disk") fi fi done < <(findmnt -rn -o SOURCE,TARGET | grep -E "^/dev/") if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "📋 System disks excluded: ${SYSTEM_DISKS[*]}" else show_message "📋 Discos del sistema excluidos: ${SYSTEM_DISKS[*]}" fi while IFS= read -r disk; do # Excluir discos del sistema, dispositivos loop, y particiones if [[ ! " ${SYSTEM_DISKS[*]} " =~ " ${disk} " ]] && \ [[ "$disk" != loop* ]] && [[ "$disk" != ram* ]] && \ [[ "$disk" != *boot* ]] && [[ ! "$disk" =~ [0-9]$ ]]; then # Verificar que sea un disco completo, no una partición if lsblk -dpno TYPE "/dev/$disk" 2>/dev/null | grep -q "disk"; then AVAILABLE_DISKS+=("$disk") # Verificar estado RAID del disco raid_status=$(check_disk_raid_status "$disk") DISK_RAID_STATUS+=("$raid_status") fi fi done < <(lsblk -dpno NAME | sed 's|/dev/||' | grep -v '^$') if [ ${#AVAILABLE_DISKS[@]} -eq 0 ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "No available disks found for RAID" else show_error "No se encontraron discos disponibles para RAID" fi exit 1 fi if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Found disks:" else show_message "Discos encontrados:" fi local disks_with_raid=false for i in "${!AVAILABLE_DISKS[@]}"; do disk="/dev/${AVAILABLE_DISKS[$i]}" size=$(lsblk -dpno SIZE "$disk" | tr -d ' ') model=$(lsblk -dpno MODEL "$disk" | tr -d ' ') status="${DISK_RAID_STATUS[$i]}" if [ -n "$status" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo -e " $((i+1)). ${AVAILABLE_DISKS[$i]} - $size - $model ${RED}[IN USE: $status]${NC}" else echo -e " $((i+1)). ${AVAILABLE_DISKS[$i]} - $size - $model ${RED}[EN USO: $status]${NC}" fi disks_with_raid=true else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " $((i+1)). ${AVAILABLE_DISKS[$i]} - $size - $model [FREE]" else echo " $((i+1)). ${AVAILABLE_DISKS[$i]} - $size - $model [LIBRE]" fi fi done # Si hay discos en RAID existentes, preguntar al usuario if [ "$disks_with_raid" = true ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "ATTENTION! Some disks are being used in existing RAID configurations." show_warning "If you continue, you will have the option to clean these disks before creating the new RAID." show_warning "This will DESTROY all data on those disks." if ! confirm "Do you want to continue with the configuration?"; then show_message "Configuration cancelled by user" exit 0 fi else show_warning "¡ATENCIÓN! Algunos discos están siendo utilizados en configuraciones RAID existentes." show_warning "Si continúas, tendrás la opción de limpiar estos discos antes de crear el nuevo RAID." show_warning "Esto DESTRUIRÁ todos los datos en esos discos." if ! confirm "¿Deseas continuar con la configuración?"; then show_message "Configuración cancelada por el usuario" exit 0 fi fi fi # Detectar NVME NVME_DISK="" for disk in "${AVAILABLE_DISKS[@]}"; do if [[ "$disk" == nvme* ]]; then NVME_DISK="$disk" show_message "Detectado disco NVME: $NVME_DISK" break fi done } # Función para seleccionar tipo de filesystem select_filesystem() { if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "Filesystem Selection" echo "Select the type of RAID filesystem:" else show_title "Selección de Sistema de Archivos" echo "Selecciona el tipo de sistema de archivos RAID:" fi echo "1. BTRFS" echo "2. ZFS" while true; do if [ "$SCRIPT_LANGUAGE" = "en" ]; then read -p "Select an option (1-2): " choice else read -p "Selecciona una opción (1-2): " choice fi case $choice in 1) FILESYSTEM_TYPE="btrfs" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "NOTE: In BTRFS, RAID 5/6 is still experimental" else show_warning "NOTA: En BTRFS, RAID 5/6 aún es experimental" fi break ;; 2) FILESYSTEM_TYPE="zfs" break ;; *) if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Invalid option. Please select 1 or 2." else echo "Opción inválida. Por favor selecciona 1 o 2." fi ;; esac done } # Función para mostrar tipos de RAID show_raid_types() { if [ "$FILESYSTEM_TYPE" = "btrfs" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Available BTRFS RAID types:" else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Available RAID types in BTRFS:" else echo "Tipos de RAID disponibles en BTRFS:" fi fi echo "1. RAID 0 (stripe) - No redundancy, maximum performance and capacity" echo "2. RAID 1 (mirror) - Data mirrored across drives, 50% capacity" echo "3. RAID 5 - Single drive fault tolerance with parity (EXPERIMENTAL)" echo "4. RAID 6 - Dual drive fault tolerance with parity (EXPERIMENTAL)" echo "5. RAID 10 - Combination of RAID 0 and 1, requires 4+ drives" else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Available ZFS RAID types:" else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Available RAID types in ZFS:" else echo "Tipos de RAID disponibles en ZFS:" fi fi echo "1. Stripe - No redundancy, maximum performance" echo "2. Mirror - Data mirrored across drives" echo "3. RAIDZ1 - Single parity, equivalent to RAID 5" echo "4. RAIDZ2 - Double parity, equivalent to RAID 6" echo "5. RAIDZ3 - Triple parity, requires 4+ drives" fi } # Función para calcular capacidad del RAID calculate_raid_capacity() { local raid_type="$1" local disk_sizes=("${@:2}") local total_capacity=0 local min_size=${disk_sizes[0]} local num_disks=${#disk_sizes[@]} # Encontrar el disco más pequeño for size in "${disk_sizes[@]}"; do if [ "$size" -lt "$min_size" ]; then min_size=$size fi total_capacity=$((total_capacity + size)) done # Calcular capacidad según tipo de RAID case $raid_type in "raid0"|"stripe") RAID_CAPACITY=$total_capacity ;; "raid1"|"mirror") RAID_CAPACITY=$min_size ;; "raid5"|"raidz1") RAID_CAPACITY=$(( min_size * (num_disks - 1) )) ;; "raid6"|"raidz2") RAID_CAPACITY=$(( min_size * (num_disks - 2) )) ;; "raid10") RAID_CAPACITY=$(( min_size * (num_disks / 2) )) ;; "raidz3") RAID_CAPACITY=$(( min_size * (num_disks - 3) )) ;; esac # Mostrar información si hay discos de diferente tamaño local sizes_differ=false for size in "${disk_sizes[@]}"; do if [ "$size" -ne "${disk_sizes[0]}" ]; then sizes_differ=true break fi done if [ "$sizes_differ" = true ]; then show_warning "¡ATENCIÓN! Los discos tienen diferentes tamaños:" for i in "${!SELECTED_DISKS[@]}"; do local disk_gb=$(( ${disk_sizes[$i]} / 1024 / 1024 / 1024 )) local disk_formatted=$(format_capacity "$disk_gb") echo " ${SELECTED_DISKS[$i]}: ${disk_formatted}" done local raid_capacity_gb=$(( RAID_CAPACITY / 1024 / 1024 / 1024 )) local raid_capacity_formatted=$(format_capacity "$raid_capacity_gb") echo "Capacidad del RAID resultante: ${raid_capacity_formatted}" echo "Se utilizará el tamaño del disco más pequeño como referencia." if ! confirm "¿Deseas continuar con esta configuración?"; then show_message "Configuración cancelada por el usuario" exit 0 fi fi } # Función para formatear capacidad en la unidad más apropiada format_capacity() { local size_gb="$1" if [ "$size_gb" -ge 1024 ]; then local size_tb=$((size_gb / 1024)) local remainder=$((size_gb % 1024)) if [ "$remainder" -eq 0 ]; then echo "${size_tb}TB" else # Calcular decimales para mostrar X.X TB local decimal=$((remainder * 10 / 1024)) echo "${size_tb}.${decimal}TB" fi else echo "${size_gb}GB" fi } # Función para mostrar vista previa de la configuración RAID show_raid_preview() { local raid_type="$1" local selected_disks=("${@:2}") local num_disks=${#selected_disks[@]} if [ $num_disks -eq 0 ]; then return 0 fi # Calcular tamaños local disk_sizes=() local total_raw_gb=0 local min_size_bytes="" for disk in "${selected_disks[@]}"; do local size_bytes=$(lsblk -dpno SIZE "/dev/$disk" --bytes) disk_sizes+=("$size_bytes") if [ -z "$min_size_bytes" ] || [ "$size_bytes" -lt "$min_size_bytes" ]; then min_size_bytes="$size_bytes" fi local size_gb=$((size_bytes / 1024 / 1024 / 1024)) total_raw_gb=$((total_raw_gb + size_gb)) done local min_size_gb=$((min_size_bytes / 1024 / 1024 / 1024)) local usable_gb=0 local redundancy_level="" local failure_tolerance="" local performance_note="" # Calcular capacidad y características según tipo de RAID case "$raid_type" in "raid0"|"stripe") usable_gb=$total_raw_gb if [ "$SCRIPT_LANGUAGE" = "en" ]; then redundancy_level="❌ NO REDUNDANCY" failure_tolerance="⚠️ 1 disk failure = TOTAL LOSS" performance_note="🚀 Maximum performance (read and write)" else redundancy_level="❌ SIN REDUNDANCIA" failure_tolerance="⚠️ Fallo de 1 disco = PÉRDIDA TOTAL" performance_note="🚀 Máximo rendimiento (lectura y escritura)" fi ;; "raid1"|"mirror") usable_gb=$min_size_gb if [ "$SCRIPT_LANGUAGE" = "en" ]; then redundancy_level="✅ FULL MIRROR" failure_tolerance="✅ Tolerates failure of up to $((num_disks-1)) disk(s)" performance_note="⚡ Good read performance, normal write" else redundancy_level="✅ ESPEJO COMPLETO" failure_tolerance="✅ Tolera fallo de hasta $((num_disks-1)) disco(s)" performance_note="⚡ Buen rendimiento de lectura, escritura normal" fi ;; "raid5"|"raidz1") usable_gb=$((min_size_gb * (num_disks - 1))) if [ "$SCRIPT_LANGUAGE" = "en" ]; then redundancy_level="✅ SINGLE PARITY" failure_tolerance="✅ Tolerates 1 disk failure" performance_note="⚡ Good balanced performance" else redundancy_level="✅ PARIDAD SIMPLE" failure_tolerance="✅ Tolera fallo de 1 disco" performance_note="⚡ Buen rendimiento balanceado" fi ;; "raid6"|"raidz2") usable_gb=$((min_size_gb * (num_disks - 2))) if [ "$SCRIPT_LANGUAGE" = "en" ]; then redundancy_level="✅ DOUBLE PARITY" failure_tolerance="✅ Tolerates up to 2 disk failures" performance_note="⚡ Moderate performance" else redundancy_level="✅ PARIDAD DOBLE" failure_tolerance="✅ Tolera fallo de hasta 2 discos" performance_note="⚡ Rendimiento moderado" fi ;; "raid10") usable_gb=$((min_size_gb * (num_disks / 2))) if [ "$SCRIPT_LANGUAGE" = "en" ]; then redundancy_level="✅ MIRROR + STRIPING" failure_tolerance="✅ Tolerates 1 disk failure per mirror" performance_note="🚀 High performance (read and write)" else redundancy_level="✅ ESPEJO + STRIPING" failure_tolerance="✅ Tolera fallo de 1 disco por espejo" performance_note="🚀 Alto rendimiento (lectura y escritura)" fi ;; "raidz3") usable_gb=$((min_size_gb * (num_disks - 3))) if [ "$SCRIPT_LANGUAGE" = "en" ]; then redundancy_level="✅ TRIPLE PARITY" failure_tolerance="✅ Tolerates up to 3 disk failures" performance_note="⚡ Conservative performance" else redundancy_level="✅ PARIDAD TRIPLE" failure_tolerance="✅ Tolera fallo de hasta 3 discos" performance_note="⚡ Rendimiento conservador" fi ;; esac local efficiency=$((usable_gb * 100 / total_raw_gb)) # Formatear capacidades local min_size_formatted=$(format_capacity "$min_size_gb") local total_raw_formatted=$(format_capacity "$total_raw_gb") local usable_formatted=$(format_capacity "$usable_gb") echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "🎯 RESULTING RAID CONFIGURATION:" echo "════════════════════════════════════════════════════════════════" echo "📦 CAPACITY:" echo " Disks: $num_disks x ${min_size_formatted}" echo " Total raw capacity: ${total_raw_formatted}" echo " Usable capacity: ${usable_formatted}" echo " Storage efficiency: ${efficiency}%" echo "" echo "🔒 DATA PROTECTION:" echo " $redundancy_level" echo " $failure_tolerance" echo "" echo "⚡ PERFORMANCE:" echo " $performance_note" else echo "🎯 CONFIGURACIÓN RAID RESULTANTE:" echo "════════════════════════════════════════════════════════════════" echo "📦 CAPACIDAD:" echo " Discos: $num_disks x ${min_size_formatted}" echo " Capacidad bruta total: ${total_raw_formatted}" echo " Capacidad utilizable: ${usable_formatted}" echo " Eficiencia de almacenamiento: ${efficiency}%" echo "" echo "🔒 PROTECCIÓN DE DATOS:" echo " $redundancy_level" echo " $failure_tolerance" echo "" echo "⚡ RENDIMIENTO:" echo " $performance_note" fi # Verificar si hay diferencias de tamaño significativas local max_size_bytes=$(printf '%s\n' "${disk_sizes[@]}" | sort -nr | head -1) local size_diff=$((max_size_bytes - min_size_bytes)) local size_variance=$((size_diff * 100 / max_size_bytes)) if [ $size_variance -gt 10 ]; then local max_size_gb=$((max_size_bytes / 1024 / 1024 / 1024)) local wasted_gb=$(((max_size_gb - min_size_gb) * num_disks)) local min_size_formatted=$(format_capacity "$min_size_gb") local max_size_formatted=$(format_capacity "$max_size_gb") local wasted_formatted=$(format_capacity "$wasted_gb") echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "⚠️ WARNING - SIZE DIFFERENCE:" echo " Smallest disk: ${min_size_formatted}" echo " Largest disk: ${max_size_formatted}" echo " Wasted space: ~${wasted_formatted}" echo " 💡 It's recommended to use similarly sized disks" else echo "⚠️ ADVERTENCIA - DIFERENCIA DE TAMAÑOS:" echo " Disco más pequeño: ${min_size_formatted}" echo " Disco más grande: ${max_size_formatted}" echo " Espacio desperdiciado: ~${wasted_formatted}" echo " 💡 Se recomienda usar discos de tamaño similar" fi fi # Advertencias específicas if [[ "$raid_type" == "raid5" || "$raid_type" == "raid6" ]] && [ "$FILESYSTEM_TYPE" = "btrfs" ]; then echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "⚠️ BTRFS WARNING:" echo " RAID 5/6 in BTRFS is EXPERIMENTAL and may cause corruption" else echo "⚠️ ADVERTENCIA BTRFS:" echo " RAID 5/6 en BTRFS es EXPERIMENTAL y puede causar corrupción" fi echo " 💡 Considera usar ZFS para RAID 5/6, o RAID 1/10 en BTRFS" fi echo "════════════════════════════════════════════════════════════════" } # Función para seleccionar discos select_disks() { if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "Disk Selection" else show_title "Selección de Discos" fi local min_disks=2 case $RAID_TYPE in "raid5"|"raidz1") min_disks=3;; "raid6"|"raidz2") min_disks=4;; "raid10") min_disks=4;; "raidz3") min_disks=4;; esac if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Select disks for RAID (minimum $min_disks disks):" echo "" echo "📖 SELECTION INSTRUCTIONS:" echo " Individual selection: Write disk number (ex: 3)" echo " Multiple selection: Write numbers separated by spaces (ex: 3 4 5 6)" echo " Range selection: Write range with dash (ex: 3-6)" echo " Toggle selection: Write disk number again to add/remove" echo "" echo "🎯 SPECIAL COMMANDS:" echo " 'clear' - Clear all selection" echo " 'done' - Finish selection and continue" echo "" echo "💡 TIP: You can modify your selection at any time by entering more disk numbers" else show_message "Selecciona los discos para el RAID (mínimo $min_disks discos):" echo "" echo "📖 INSTRUCCIONES DE SELECCIÓN:" echo " Selección individual: Escribe el número del disco (ej: 3)" echo " Selección múltiple: Escribe números separados por espacios (ej: 3 4 5 6)" echo " Selección por rango: Escribe rango con guión (ej: 3-6)" echo " Alternar selección: Vuelve a escribir el número del disco para agregar/quitar" echo "" echo "🎯 COMANDOS ESPECIALES:" echo " 'clear' - Limpiar toda la selección" echo " 'done' - Finalizar selección y continuar" echo "" echo "💡 CONSEJO: Puedes modificar tu selección en cualquier momento ingresando más números de disco" fi echo "" SELECTED_DISKS=() SELECTED_DISK_SIZES=() DISKS_TO_CLEAN=() while true; do if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Available disks:" else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Available disks:" else echo "Discos disponibles:" fi fi for i in "${!AVAILABLE_DISKS[@]}"; do disk="/dev/${AVAILABLE_DISKS[$i]}" size=$(lsblk -dpno SIZE "$disk" | tr -d ' ') status="${DISK_RAID_STATUS[$i]}" if [ -n "$status" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo -e " $((i+1)). ${AVAILABLE_DISKS[$i]} - $size ${RED}[IN USE: $status]${NC}" else echo -e " $((i+1)). ${AVAILABLE_DISKS[$i]} - $size ${RED}[EN USO: $status]${NC}" fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " $((i+1)). ${AVAILABLE_DISKS[$i]} - $size [FREE]" else echo " $((i+1)). ${AVAILABLE_DISKS[$i]} - $size [LIBRE]" fi fi done echo "" if [ ${#SELECTED_DISKS[@]} -gt 0 ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "✅ Selected disks (${#SELECTED_DISKS[@]}/${#AVAILABLE_DISKS[@]}): ${SELECTED_DISKS[*]}" else echo "✅ Discos seleccionados (${#SELECTED_DISKS[@]}/${#AVAILABLE_DISKS[@]}): ${SELECTED_DISKS[*]}" fi # Mostrar vista previa de la configuración RAID show_raid_preview "$RAID_TYPE" "${SELECTED_DISKS[@]}" else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "⭕ Selected disks: none" else echo "⭕ Discos seleccionados: ninguno" fi fi echo "" # Show available commands reminder if [ ${#SELECTED_DISKS[@]} -ge $min_disks ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 AVAILABLE COMMANDS:" echo " • 'done' - Finish selection and continue" echo " • 'clear' - Clear all selected disks" echo " • Add more disks: Enter disk number (ex: 1, 2)" echo " • Remove disks: Enter selected disk number again" echo " • Use ranges: Enter range (ex: 1-2)" else echo "💡 COMANDOS DISPONIBLES:" echo " • 'done' - Finalizar selección y continuar" echo " • 'clear' - Limpiar todos los discos seleccionados" echo " • Agregar más discos: Ingresa número de disco (ej: 1, 2)" echo " • Quitar discos: Ingresa número de disco seleccionado otra vez" echo " • Usar rangos: Ingresa rango (ej: 1-2)" fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 AVAILABLE COMMANDS:" echo " • Enter disk numbers: Individual (ex: 3), multiple (ex: 3 4 5), ranges (ex: 3-6)" echo " • 'clear' - Reset selection" echo " • Need minimum $min_disks disks to continue" else echo "💡 COMANDOS DISPONIBLES:" echo " • Ingresa números de disco: Individual (ej: 3), múltiple (ej: 3 4 5), rangos (ej: 3-6)" echo " • 'clear' - Resetear selección" echo " • Se necesitan mínimo $min_disks discos para continuar" fi fi echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then read -p "👉 Selection: " choice else read -p "👉 Selección: " choice fi if [ "$choice" = "done" ]; then if [ ${#SELECTED_DISKS[@]} -ge $min_disks ]; then break else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "❌ You need at least $min_disks disks for this RAID" else show_error "❌ Necesitas al menos $min_disks discos para este RAID" fi fi elif [ "$choice" = "clear" ]; then SELECTED_DISKS=() SELECTED_DISK_SIZES=() DISKS_TO_CLEAN=() if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "🧹 Selection cleared" else show_message "🧹 Selección limpiada" fi elif [[ "$choice" =~ ^[0-9]+(-[0-9]+)?$ ]]; then # Manejar rangos (ej: 3-6) if [[ "$choice" =~ - ]]; then local start=$(echo "$choice" | cut -d'-' -f1) local end=$(echo "$choice" | cut -d'-' -f2) if [ "$start" -ge 1 ] && [ "$end" -le ${#AVAILABLE_DISKS[@]} ] && [ "$start" -le "$end" ]; then for ((i=start; i<=end; i++)); do local disk_index=$((i-1)) local disk="${AVAILABLE_DISKS[$disk_index]}" local disk_status="${DISK_RAID_STATUS[$disk_index]}" # Procesar disco (similar a la lógica individual) if [[ ! " ${SELECTED_DISKS[*]} " =~ " ${disk} " ]]; then if [ -n "$disk_status" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "⚠️ Disk $disk is in use: $disk_status" if confirm "Clean $disk and add it?"; then SELECTED_DISKS+=("$disk") disk_size=$(lsblk -dpno SIZE "/dev/$disk" --bytes) SELECTED_DISK_SIZES+=("$disk_size") DISKS_TO_CLEAN+=("$disk") fi else show_warning "⚠️ Disco $disk está en uso: $disk_status" if confirm "¿Limpiar $disk y agregarlo?"; then SELECTED_DISKS+=("$disk") disk_size=$(lsblk -dpno SIZE "/dev/$disk" --bytes) SELECTED_DISK_SIZES+=("$disk_size") DISKS_TO_CLEAN+=("$disk") fi fi else SELECTED_DISKS+=("$disk") disk_size=$(lsblk -dpno SIZE "/dev/$disk" --bytes) SELECTED_DISK_SIZES+=("$disk_size") fi fi done if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ Processed range $start-$end" else show_message "✅ Procesado rango $start-$end" fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "❌ Invalid range. Use format: start-end (ex: 3-6)" else echo "❌ Rango inválido. Usa formato: inicio-fin (ej: 3-6)" fi fi else # Selección individual (lógica original) if [ "$choice" -ge 1 ] && [ "$choice" -le ${#AVAILABLE_DISKS[@]} ]; then disk_index=$((choice-1)) disk="${AVAILABLE_DISKS[$disk_index]}" disk_status="${DISK_RAID_STATUS[$disk_index]}" # Verificar si ya está seleccionado if [[ " ${SELECTED_DISKS[*]} " =~ " ${disk} " ]]; then # Quitar de la selección new_selected=() new_sizes=() new_to_clean=() for i in "${!SELECTED_DISKS[@]}"; do if [ "${SELECTED_DISKS[$i]}" != "$disk" ]; then new_selected+=("${SELECTED_DISKS[$i]}") new_sizes+=("${SELECTED_DISK_SIZES[$i]}") fi done SELECTED_DISKS=("${new_selected[@]}") SELECTED_DISK_SIZES=("${new_sizes[@]}") # Remover de discos a limpiar si estaba ahí for i in "${!DISKS_TO_CLEAN[@]}"; do if [ "${DISKS_TO_CLEAN[$i]}" != "$disk" ]; then new_to_clean+=("${DISKS_TO_CLEAN[$i]}") fi done DISKS_TO_CLEAN=("${new_to_clean[@]}") if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "➖ Removed: $disk" else show_message "➖ Removido: $disk" fi else # Verificar si el disco está en uso if [ -n "$disk_status" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "⚠️ Disk $disk is in use: $disk_status" show_warning "⚠️ If you continue, this disk will be completely cleaned and ALL data will be lost." if confirm "Do you want to clean this disk and use it for the new RAID?"; then SELECTED_DISKS+=("$disk") disk_size=$(lsblk -dpno SIZE "/dev/$disk" --bytes) SELECTED_DISK_SIZES+=("$disk_size") DISKS_TO_CLEAN+=("$disk") show_message "✅ Added (will be cleaned): $disk" else show_message "❌ Disk $disk not selected" fi else show_warning "⚠️ El disco $disk está en uso: $disk_status" show_warning "⚠️ Si continúas, este disco será completamente limpiado y se perderán TODOS los datos." if confirm "¿Deseas limpiar este disco y usarlo para el nuevo RAID?"; then SELECTED_DISKS+=("$disk") disk_size=$(lsblk -dpno SIZE "/dev/$disk" --bytes) SELECTED_DISK_SIZES+=("$disk_size") DISKS_TO_CLEAN+=("$disk") show_message "✅ Agregado (se limpiará): $disk" else show_message "❌ Disco $disk no seleccionado" fi fi else # Agregar a la selección (disco libre) SELECTED_DISKS+=("$disk") disk_size=$(lsblk -dpno SIZE "/dev/$disk" --bytes) SELECTED_DISK_SIZES+=("$disk_size") if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ Added: $disk" else show_message "✅ Agregado: $disk" fi fi fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "❌ Invalid number. Use 1-${#AVAILABLE_DISKS[@]}" else echo "❌ Número inválido. Usa 1-${#AVAILABLE_DISKS[@]}" fi fi fi elif [[ "$choice" =~ ^[0-9\ ]+$ ]]; then # Manejar múltiples números separados por espacios (ej: 3 4 5 6) for num in $choice; do if [ "$num" -ge 1 ] && [ "$num" -le ${#AVAILABLE_DISKS[@]} ]; then disk_index=$((num-1)) disk="${AVAILABLE_DISKS[$disk_index]}" disk_status="${DISK_RAID_STATUS[$disk_index]}" if [[ ! " ${SELECTED_DISKS[*]} " =~ " ${disk} " ]]; then if [ -n "$disk_status" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "⚠️ Disk $disk is in use: $disk_status" if confirm "Clean $disk and add it?"; then SELECTED_DISKS+=("$disk") disk_size=$(lsblk -dpno SIZE "/dev/$disk" --bytes) SELECTED_DISK_SIZES+=("$disk_size") DISKS_TO_CLEAN+=("$disk") show_message "✅ Added (will be cleaned): $disk" fi else show_warning "⚠️ Disco $disk está en uso: $disk_status" if confirm "¿Limpiar $disk y agregarlo?"; then SELECTED_DISKS+=("$disk") disk_size=$(lsblk -dpno SIZE "/dev/$disk" --bytes) SELECTED_DISK_SIZES+=("$disk_size") DISKS_TO_CLEAN+=("$disk") show_message "✅ Agregado (se limpiará): $disk" fi fi else SELECTED_DISKS+=("$disk") disk_size=$(lsblk -dpno SIZE "/dev/$disk" --bytes) SELECTED_DISK_SIZES+=("$disk_size") if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ Added: $disk" else show_message "✅ Agregado: $disk" fi fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "ℹ️ $disk was already selected" else show_message "ℹ️ $disk ya estaba seleccionado" fi fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "❌ Invalid number: $num (use 1-${#AVAILABLE_DISKS[@]})" else echo "❌ Número inválido: $num (usa 1-${#AVAILABLE_DISKS[@]})" fi fi done else if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Invalid option" else echo "Opción inválida" fi fi done # Si hay discos para limpiar, mostrar resumen y confirmar if [ ${#DISKS_TO_CLEAN[@]} -gt 0 ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "SUMMARY OF DISKS TO CLEAN:" else show_warning "RESUMEN DE DISCOS A LIMPIAR:" fi for disk in "${DISKS_TO_CLEAN[@]}"; do # Encontrar el índice del disco para mostrar su estado for i in "${!AVAILABLE_DISKS[@]}"; do if [ "${AVAILABLE_DISKS[$i]}" = "$disk" ]; then echo -e " - $disk: ${DISK_RAID_STATUS[$i]}" break fi done done if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "ALL DATA ON THESE DISKS WILL BE PERMANENTLY LOST!" if ! confirm "Are you completely sure you want to continue?"; then show_message "Operation cancelled by user" exit 0 fi else show_warning "¡TODOS LOS DATOS EN ESTOS DISCOS SE PERDERÁN PERMANENTEMENTE!" if ! confirm "¿Estás completamente seguro de que deseas continuar?"; then show_message "Operación cancelada por el usuario" exit 0 fi fi # Proceder con la limpieza for disk in "${DISKS_TO_CLEAN[@]}"; do clean_disk "$disk" done if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "All disks have been cleaned successfully" else show_message "Todos los discos han sido limpiados exitosamente" fi fi # Calcular capacidad del RAID calculate_raid_capacity "$RAID_TYPE" "${SELECTED_DISK_SIZES[@]}" } # Función para configurar BTRFS setup_btrfs() { if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "Configuring BTRFS RAID" else show_title "Configurando BTRFS RAID" fi # Seleccionar tipo de RAID show_raid_types while true; do if [ "$SCRIPT_LANGUAGE" = "en" ]; then read -p "Select RAID type (1-5): " choice else read -p "Selecciona el tipo de RAID (1-5): " choice fi case $choice in 1) RAID_TYPE="raid0"; break;; 2) RAID_TYPE="raid1"; break;; 3) RAID_TYPE="raid5"; if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "RAID 5 is experimental in BTRFS" if confirm "Do you want to continue?"; then break; else continue; fi else show_warning "RAID 5 es experimental en BTRFS" if confirm "¿Deseas continuar?"; then break; else continue; fi fi;; 4) RAID_TYPE="raid6"; if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning "RAID 6 is experimental in BTRFS" if confirm "Do you want to continue?"; then break; else continue; fi else show_warning "RAID 6 es experimental en BTRFS" if confirm "¿Deseas continuar?"; then break; else continue; fi fi;; 5) RAID_TYPE="raid10"; break;; *) if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Invalid option" else echo "Opción inválida" fi;; esac done select_disks # Solicitar punto de montaje read -p "Ingresa la ruta donde quieres montar el RAID (ej: /mnt/raid): " MOUNT_POINT if [ ! -d "$MOUNT_POINT" ]; then if confirm "El directorio $MOUNT_POINT no existe. ¿Deseas crearlo?"; then sudo mkdir -p "$MOUNT_POINT" show_message "Directorio $MOUNT_POINT creado" else show_error "Se necesita un punto de montaje válido" exit 1 fi fi # Crear el filesystem BTRFS show_message "Creando filesystem BTRFS..." device_list="" for disk in "${SELECTED_DISKS[@]}"; do device_list="$device_list /dev/$disk" done sudo mkfs.btrfs -f -d "$RAID_TYPE" -m "$RAID_TYPE" $device_list # Montar el filesystem sudo mount "/dev/${SELECTED_DISKS[0]}" "$MOUNT_POINT" show_message "BTRFS RAID configurado exitosamente" FILESYSTEM_UUID=$(sudo blkid -s UUID -o value "/dev/${SELECTED_DISKS[0]}") } # Función para obtener RAM del sistema get_system_ram() { local ram_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}') local ram_gb=$((ram_kb / 1024 / 1024)) echo $ram_gb } # Función para configurar ZFS setup_zfs() { if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_title "Configuring ZFS RAID" else show_title "Configurando ZFS RAID" fi # Verificar que ZFS esté disponible (debería estarlo después de check_and_install_requirements) if ! command -v zpool &> /dev/null; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "ZFS is not available. This should not happen after requirements verification." else show_error "ZFS no está disponible. Esto no debería suceder después de la verificación de requisitos." fi exit 1 fi # Verificar que el módulo ZFS esté cargado if ! lsmod | grep -q "^zfs "; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Loading ZFS module..." else show_message "Cargando módulo ZFS..." fi sudo modprobe zfs sleep 2 if ! lsmod | grep -q "^zfs "; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "Could not load ZFS module" else show_error "No se pudo cargar el módulo ZFS" fi exit 1 fi fi # Seleccionar tipo de RAID show_raid_types while true; do if [ "$SCRIPT_LANGUAGE" = "en" ]; then read -p "Select RAID type (1-5): " choice else read -p "Selecciona el tipo de RAID (1-5): " choice fi case $choice in 1) RAID_TYPE="stripe"; break;; 2) RAID_TYPE="mirror"; break;; 3) RAID_TYPE="raidz1"; break;; 4) RAID_TYPE="raidz2"; break;; 5) RAID_TYPE="raidz3"; break;; *) echo "Opción inválida";; esac done select_disks # Solicitar nombre del pool if [ "$SCRIPT_LANGUAGE" = "en" ]; then read -p "Enter ZFS pool name: " POOL_NAME else read -p "Ingresa el nombre del pool ZFS: " POOL_NAME fi # Configurar ARC local system_ram=$(get_system_ram) local recommended_arc=$((system_ram / 4)) if [ $recommended_arc -lt 1 ]; then recommended_arc=1 fi if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "System RAM: ${system_ram}GB" show_message "Recommended ARC: ${recommended_arc}GB" read -p "How many GB do you want to assign to ARC? [$recommended_arc]: " arc_size else show_message "RAM del sistema: ${system_ram}GB" show_message "ARC recomendado: ${recommended_arc}GB" read -p "¿Cuántos GB quieres asignar al ARC? [$recommended_arc]: " arc_size fi arc_size=${arc_size:-$recommended_arc} # Detectar ashift óptimo considerando compatibilidad futura local optimal_ashift=12 # Default para compatibilidad con cache devices local max_sector_size=512 local has_4k_sectors=false # Detectar el tamaño de sector más grande entre los discos seleccionados for disk in "${SELECTED_DISKS[@]}"; do if [ -b "/dev/$disk" ]; then local sector_size=$(sudo blockdev --getpbsz "/dev/$disk" 2>/dev/null) if [ -n "$sector_size" ]; then if [ "$sector_size" -gt "$max_sector_size" ]; then max_sector_size=$sector_size fi if [ "$sector_size" -eq 4096 ]; then has_4k_sectors=true fi fi fi done # Estrategia de ashift optimizada para compatibilidad if [ "$max_sector_size" -le 512 ] && [ "$has_4k_sectors" = false ]; then # Solo discos 512-byte: usar ashift=12 por compatibilidad con cache futuro optimal_ashift=12 show_message "[INFO] 🔧 Usando ashift=12 para compatibilidad con cache devices SSD" else # Calcular ashift basado en el tamaño de sector más grande case $max_sector_size in 512) optimal_ashift=12 ;; # Forzar 12 para compatibilidad 1024) optimal_ashift=10 ;; # 2^10 = 1024 bytes 2048) optimal_ashift=11 ;; # 2^11 = 2048 bytes 4096) optimal_ashift=12 ;; # 2^12 = 4096 bytes (óptimo) 8192) optimal_ashift=13 ;; # 2^13 = 8192 bytes *) optimal_ashift=12 ;; # Default seguro esac fi show_message "$(get_text "sector_config_detected")" show_message "$(get_text "max_sector_size_hdds") ${max_sector_size} bytes" show_message "$(get_text "pool_ashift") ${optimal_ashift} (2^${optimal_ashift} = $((2**optimal_ashift)) bytes)" show_message "$(get_text "compatibility_cache_devices")" # Crear el pool ZFS if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Creating ZFS pool..." else show_message "Creando pool ZFS..." fi # Preparar discos (limpiar filesystems existentes) if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Preparing disks (wiping existing filesystems)..." else show_message "Preparando discos (limpiando filesystems existentes)..." fi for disk in "${SELECTED_DISKS[@]}"; do clean_disk "$disk" done device_list="" for disk in "${SELECTED_DISKS[@]}"; do device_list="$device_list /dev/$disk" done case $RAID_TYPE in "stripe") sudo zpool create -f -o ashift=$optimal_ashift "$POOL_NAME" $device_list ;; "mirror") sudo zpool create -f -o ashift=$optimal_ashift "$POOL_NAME" mirror $device_list ;; "raidz1") sudo zpool create -f -o ashift=$optimal_ashift "$POOL_NAME" raidz $device_list ;; "raidz2") sudo zpool create -f -o ashift=$optimal_ashift "$POOL_NAME" raidz2 $device_list ;; "raidz3") sudo zpool create -f -o ashift=$optimal_ashift "$POOL_NAME" raidz3 $device_list ;; esac # Configurar ARC echo $((arc_size * 1024 * 1024 * 1024)) | sudo tee /sys/module/zfs/parameters/zfs_arc_max > /dev/null # Hacer persistente la configuración del ARC sudo mkdir -p /etc/modprobe.d echo "options zfs zfs_arc_max=$((arc_size * 1024 * 1024 * 1024))" | sudo tee /etc/modprobe.d/zfs.conf > /dev/null MOUNT_POINT="/$POOL_NAME" # Configurar NVME para cache y log si está disponible if [ -n "$NVME_DISK" ] && [[ ! " ${SELECTED_DISKS[*]} " =~ " ${NVME_DISK} " ]]; then if confirm "¿Deseas usar el NVME ($NVME_DISK) para ARC2 (L2ARC) y SLOG?"; then show_message "Particionando NVME para cache y log..." # Crear particiones en el NVME sudo parted "/dev/$NVME_DISK" --script mklabel gpt sudo parted "/dev/$NVME_DISK" --script mkpart primary 0% 50% sudo parted "/dev/$NVME_DISK" --script mkpart primary 50% 100% sleep 2 # Agregar cache y log al pool sudo zpool add "$POOL_NAME" cache "/dev/${NVME_DISK}p1" sudo zpool add "$POOL_NAME" log "/dev/${NVME_DISK}p2" if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "NVME configured as cache and log" else show_message "NVME configurado como cache y log" fi fi fi if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "ZFS pool configured successfully" else show_message "ZFS pool configurado exitosamente" fi # Crear datasets dentro del pool principal usando la función reutilizable echo "" if confirm "¿Deseas crear datasets dentro del pool '$POOL_NAME'?"; then create_datasets_in_pool "$POOL_NAME" fi # Preguntar por pools adicionales solo si hay discos restantes remaining_disks=() for disk in "${AVAILABLE_DISKS[@]}"; do if [[ ! " ${SELECTED_DISKS[*]} " =~ " ${disk} " ]] && [ "$disk" != "$NVME_DISK" ]; then # Verificar que el disco no sea demasiado pequeño (ignorar mmcblk0boot) if [[ ! "$disk" =~ mmcblk.*boot ]]; then remaining_disks+=("$disk") fi fi done if [ ${#remaining_disks[@]} -gt 0 ]; then echo "" show_message "Discos disponibles restantes:" for disk in "${remaining_disks[@]}"; do size=$(lsblk -dpno SIZE "/dev/$disk" | tr -d ' ') model=$(lsblk -dpno MODEL "/dev/$disk" | tr -d ' ') echo " - $disk ($size, $model)" done echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 ADDITIONAL POOLS INFORMATION:" else echo "💡 INFORMACIÓN SOBRE POOLS ADICIONALES:" fi echo " Los pools adicionales son independientes del pool principal." echo " Útiles para:" echo " • Separar datos por uso (backup, cache, temp)" echo " • Diferentes niveles de redundancia" echo " • Optimizar rendimiento por tipo de carga" echo "" echo " Ejemplo: Pool principal para datos + Pool SSD para cache" echo "" if confirm "¿Deseas crear pools ZFS adicionales con los discos restantes?"; then # Implementar creación de pools adicionales while [ ${#remaining_disks[@]} -gt 0 ]; do echo "" show_title "Configuración de Pool Adicional" read -p "Nombre del nuevo pool: " additional_pool_name if [ -z "$additional_pool_name" ]; then show_error "El nombre del pool no puede estar vacío" continue fi # Verificar que el nombre no exista if zpool list -H -o name 2>/dev/null | grep -q "^${additional_pool_name}$"; then show_error "Ya existe un pool con el nombre '$additional_pool_name'" continue fi if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Available disks for pool '$additional_pool_name':" else echo "Discos disponibles para el pool '$additional_pool_name':" fi for i in "${!remaining_disks[@]}"; do disk="${remaining_disks[$i]}" size=$(lsblk -dpno SIZE "/dev/$disk" | tr -d ' ') model=$(lsblk -dpno MODEL "/dev/$disk" | tr -d ' ') echo " $((i+1)). $disk - $size - $model" done echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "Available RAID types:" else echo "Tipos de RAID disponibles:" fi echo "1. Stripe - Sin redundancia, máximo rendimiento" echo "2. Mirror - Datos duplicados entre discos" echo "3. RAIDZ1 - Un disco de paridad (mínimo 3 discos)" echo "4. RAIDZ2 - Dos discos de paridad (mínimo 4 discos)" read -p "Selecciona tipo de RAID (1-4): " raid_choice local additional_raid_type="" local min_disks_needed=1 case $raid_choice in 1) additional_raid_type="stripe"; min_disks_needed=1;; 2) additional_raid_type="mirror"; min_disks_needed=2;; 3) additional_raid_type="raidz1"; min_disks_needed=3;; 4) additional_raid_type="raidz2"; min_disks_needed=4;; *) show_error "Opción inválida" continue ;; esac if [ ${#remaining_disks[@]} -lt $min_disks_needed ]; then show_error "Se necesitan al menos $min_disks_needed discos para $additional_raid_type" continue fi # Selección simple para pools adicionales selected_additional_disks=() echo "" echo "Selecciona discos para '$additional_pool_name' (mínimo $min_disks_needed):" echo "Ingresa números separados por espacios (ej: 1 2 3):" read -p "Selección: " disk_selection selected_additional_disks=() for num in $disk_selection; do if [[ "$num" =~ ^[0-9]+$ ]] && [ "$num" -ge 1 ] && [ "$num" -le ${#remaining_disks[@]} ]; then idx=$((num-1)) selected_additional_disks+=("${remaining_disks[$idx]}") fi done if [ ${#selected_additional_disks[@]} -lt $min_disks_needed ]; then show_error "Se necesitan al menos $min_disks_needed discos" continue fi # Crear el pool adicional show_message "Creando pool adicional '$additional_pool_name'..." # Detectar ashift óptimo para el pool adicional local additional_optimal_ashift=12 # Default para compatibilidad local additional_max_sector_size=512 local additional_has_4k_sectors=false # Detectar el tamaño de sector más grande entre los discos seleccionados for disk in "${selected_additional_disks[@]}"; do if [ -b "/dev/$disk" ]; then local sector_size=$(sudo blockdev --getpbsz "/dev/$disk" 2>/dev/null) if [ -n "$sector_size" ]; then if [ "$sector_size" -gt "$additional_max_sector_size" ]; then additional_max_sector_size=$sector_size fi if [ "$sector_size" -eq 4096 ]; then additional_has_4k_sectors=true fi fi fi done # Estrategia de ashift optimizada para compatibilidad if [ "$additional_max_sector_size" -le 512 ] && [ "$additional_has_4k_sectors" = false ]; then # Solo discos 512-byte: usar ashift=12 por compatibilidad additional_optimal_ashift=12 show_message "[INFO] 🔧 Usando ashift=12 para compatibilidad con cache devices" else # Calcular ashift basado en el tamaño de sector más grande case $additional_max_sector_size in 512) additional_optimal_ashift=12 ;; # Forzar 12 para compatibilidad 1024) additional_optimal_ashift=10 ;; 2048) additional_optimal_ashift=11 ;; 4096) additional_optimal_ashift=12 ;; 8192) additional_optimal_ashift=13 ;; *) additional_optimal_ashift=12 ;; esac fi show_message "$(get_text "config_for_pool") '$additional_pool_name':" show_message "$(get_text "max_sector_size_hdds") ${additional_max_sector_size} bytes" show_message "$(get_text "pool_ashift") ${additional_optimal_ashift} (2^${additional_optimal_ashift} = $((2**additional_optimal_ashift)) bytes)" show_message "$(get_text "ensures_compatibility_future")" # Limpiar discos seleccionados antes de crear el pool for disk in "${selected_additional_disks[@]}"; do if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "🧹 Cleaning disk /dev/$disk before using it..." else show_message "🧹 Limpiando disco /dev/$disk antes de usarlo..." fi clean_disk "$disk" done local device_list="" for disk in "${selected_additional_disks[@]}"; do device_list="$device_list /dev/$disk" done case $additional_raid_type in "stripe") sudo zpool create -f -o ashift=$additional_optimal_ashift "$additional_pool_name" $device_list ;; "mirror") sudo zpool create -f -o ashift=$additional_optimal_ashift "$additional_pool_name" mirror $device_list ;; "raidz1") sudo zpool create -f -o ashift=$additional_optimal_ashift "$additional_pool_name" raidz $device_list ;; "raidz2") sudo zpool create -f -o ashift=$additional_optimal_ashift "$additional_pool_name" raidz2 $device_list ;; esac if [ $? -eq 0 ]; then show_message "✅ Pool '$additional_pool_name' creado exitosamente" # Remover discos usados de la lista de disponibles new_remaining=() for disk in "${remaining_disks[@]}"; do if [[ ! " ${selected_additional_disks[*]} " =~ " ${disk} " ]]; then new_remaining+=("$disk") fi done remaining_disks=("${new_remaining[@]}") if [ ${#remaining_disks[@]} -eq 0 ]; then show_message "No quedan más discos disponibles" break fi if ! confirm "¿Crear otro pool adicional?"; then break fi else show_error "❌ Error creando el pool '$additional_pool_name'" fi done fi else show_message "ℹ️ No hay discos adicionales disponibles para más pools" fi } # Función para mostrar resumen show_summary() { show_title "Resumen de Configuración" echo "Sistema de archivos: $FILESYSTEM_TYPE" echo "Tipo de RAID: $RAID_TYPE" echo "Discos utilizados: ${SELECTED_DISKS[*]}" echo "Punto de montaje: $MOUNT_POINT" if [ "$FILESYSTEM_TYPE" = "zfs" ]; then echo "Nombre del pool: $POOL_NAME" echo "ARC configurado: ${arc_size}GB" fi local raid_capacity_gb=$(( RAID_CAPACITY / 1024 / 1024 / 1024 )) local raid_capacity_formatted=$(format_capacity "$raid_capacity_gb") echo "Capacidad aproximada del RAID: ${raid_capacity_formatted}" } # Función para configurar montaje automático setup_auto_mount() { if confirm "¿Deseas montar automáticamente al iniciar el sistema?"; then if [ "$FILESYSTEM_TYPE" = "btrfs" ]; then # Agregar entrada a /etc/fstab para BTRFS echo "UUID=$FILESYSTEM_UUID $MOUNT_POINT btrfs defaults 0 0" | sudo tee -a /etc/fstab show_message "Montaje automático configurado en /etc/fstab" else # Para ZFS, habilitar el servicio de importación automática sudo systemctl enable zfs-import-cache sudo systemctl enable zfs-mount sudo systemctl enable zfs.target show_message "Servicios ZFS habilitados para montaje automático" fi fi } # Función principal main() { # Select language first / Seleccionar idioma primero select_language show_title "$(get_text "script_title")" # Check if running as root or with sudo / Verificar si se ejecuta como root o con sudo if [ "$EUID" -eq 0 ]; then show_error "$(get_text "root_warning")" exit 1 fi # Check and install requirements / Verificar e instalar requisitos check_and_install_requirements # Detect existing RAID configurations (ZFS, BTRFS, MDADM, LVM) # Detectar configuraciones RAID existentes (ZFS, BTRFS, MDADM, LVM) if detect_existing_raid_configurations; then # Continue with normal configuration / Continúa con configuración normal detect_disks select_filesystem if [ "$FILESYSTEM_TYPE" = "btrfs" ]; then setup_btrfs else setup_zfs fi show_summary setup_auto_mount show_message "$(get_text "raid_completed")" show_message "$(get_text "raid_mounted_at") $MOUNT_POINT" # Offer dataset management after creating the pool # Ofrecer gestión de datasets después de crear el pool offer_post_creation_dataset_management fi # If detected existing RAID configurations and user chose to manage them, # the detect_existing_raid_configurations function already handled everything # Si detectó configuraciones RAID existentes y el usuario eligió gestionarlas, # la función detect_existing_raid_configurations ya manejó todo } # Función para ofrecer gestión de datasets después de crear un pool offer_post_creation_dataset_management() { if [ "$FILESYSTEM_TYPE" = "zfs" ] && [ -n "$POOL_NAME" ]; then echo "" show_title "Gestión Post-Creación" echo "" echo "🎉 ¡Pool ZFS '$POOL_NAME' creado exitosamente!" echo "" # Configurar atime primero (recomendado) configure_atime_settings "$POOL_NAME" while true; do echo "" echo "🛠️ OPCIONES ADICIONALES:" echo " 1. 🚀 Configurar dispositivos de cache (L2ARC/SLOG) - RECOMENDADO" echo " 2. 📁 Gestionar datasets en '$POOL_NAME'" echo " 3. 📸 Crear snapshots iniciales" echo " 4. 📊 Ver estado completo del pool" echo " 5. ⚙️ Reconfigurar atime" echo " 6. ✅ Finalizar" echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 NOTE: Cache devices (option 1) must be SSD or NVMe" else echo "💡 NOTA: Los dispositivos de cache (opción 1) deben ser SSD o NVMe" fi echo " ⚠️ NO usar discos mecánicos - pueden empeorar el rendimiento" echo "" read -p "👉 Selecciona una opción (1-6): " choice case $choice in 1) echo "" setup_cache_devices "$POOL_NAME" ;; 2) echo "" create_datasets_in_pool "$POOL_NAME" ;; 3) create_initial_snapshots "$POOL_NAME" ;; 4) if [ -z "$POOL_NAME" ]; then show_error "Error: POOL_NAME no está definido. Esto no debería ocurrir." echo "Información de debug:" echo " FILESYSTEM_TYPE: $FILESYSTEM_TYPE" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " Available pools: $(zpool list -H -o name 2>/dev/null | tr '\n' ' ')" else echo " Pools disponibles: $(zpool list -H -o name 2>/dev/null | tr '\n' ' ')" fi read -p "Presiona Enter para continuar..." else show_pool_status "$POOL_NAME" fi ;; 5) echo "" configure_atime_settings "$POOL_NAME" ;; 6) show_message "¡Configuración completada!" echo "" echo "📊 RESUMEN FINAL:" echo " Pool ZFS: $POOL_NAME" echo " Estado: $(zpool list -H -o health "$POOL_NAME" 2>/dev/null || echo 'Desconocido')" echo " Tamaño: $(zpool list -H -o size "$POOL_NAME" 2>/dev/null || echo 'Desconocido')" echo " Usado: $(zpool list -H -o alloc "$POOL_NAME" 2>/dev/null || echo 'Desconocido')" echo " Libre: $(zpool list -H -o free "$POOL_NAME" 2>/dev/null || echo 'Desconocido')" echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "✅ ZFS RAID configuration completed successfully!" else echo "✅ Configuración de RAID ZFS completada exitosamente!" fi break ;; *) echo "❌ Opción inválida. Selecciona 1, 2, 3, 4, 5 o 6." ;; esac echo "" done fi # Asegurar que las variables estén definidas para evitar errores if [ -z "$POOL_NAME" ] && [ "$FILESYSTEM_TYPE" = "zfs" ]; then echo "⚠️ ADVERTENCIA: POOL_NAME no está definido al final de la gestión post-creación." if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " This could indicate a problem in the script." else echo " Esto podría indicar un problema en el script." fi fi } # Función para configurar atime en ZFS configure_atime_settings() { local pool_name="$1" show_title "Configuración de Atime en ZFS" echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 ATIME INFORMATION:" else echo "💡 INFORMACIÓN SOBRE ATIME:" fi echo " atime (access time) registra la última vez que se accedió a un archivo." echo " En sistemas con muchas operaciones de lectura, puede impactar el rendimiento." echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "📊 AVAILABLE OPTIONS IN ZFS:" else echo "📊 OPCIONES DISPONIBLES EN ZFS:" fi echo " 1. off - No registrar atime (RECOMENDADO para rendimiento)" echo " 2. on - Registrar atime completo (puede reducir rendimiento)" echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 RECOMMENDATION: Option 1 (off) for maximum performance" else echo "💡 RECOMENDACIÓN: Opción 1 (off) para máximo rendimiento" fi echo " ⚠️ NOTA: ZFS solo soporta 'on' u 'off' (no soporta 'relatime')" echo " La mayoría de aplicaciones no necesitan atime y deshabilitarlo" echo " mejora significativamente el rendimiento de lectura." echo "" while true; do read -p "👉 Selecciona configuración de atime (1-2) [1]: " atime_choice atime_choice=${atime_choice:-1} case $atime_choice in 1) local atime_setting="off" local description="Deshabilitado (máximo rendimiento)" break ;; 2) local atime_setting="on" local description="Habilitado (impacto en rendimiento)" break ;; *) echo "❌ Opción inválida. Selecciona 1 o 2." ;; esac done if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Configuring atime=$atime_setting on pool '$pool_name'..." else show_message "Configurando atime=$atime_setting en el pool '$pool_name'..." fi # Aplicar configuración al pool raíz if sudo zfs set atime="$atime_setting" "$pool_name"; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "✅ atime configured as '$atime_setting' on '$pool_name'" else show_message "✅ atime configurado como '$atime_setting' en '$pool_name'" fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_error "❌ Error configuring atime on '$pool_name'" else show_error "❌ Error configurando atime en '$pool_name'" fi return 1 fi # Aplicar a todos los datasets existentes local datasets=$(zfs list -H -o name -r "$pool_name" 2>/dev/null | grep -v "^${pool_name}$") if [ -n "$datasets" ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message "Applying configuration to existing datasets..." else show_message "Aplicando configuración a datasets existentes..." fi for dataset in $datasets; do if sudo zfs set atime="$atime_setting" "$dataset"; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_message " ✅ $dataset configured" else show_message " ✅ $dataset configurado" fi else if [ "$SCRIPT_LANGUAGE" = "en" ]; then show_warning " ⚠️ Error configuring $dataset" else show_warning " ⚠️ Error configurando $dataset" fi fi done fi echo "" show_message "📊 CONFIGURACIÓN ATIME COMPLETADA:" echo " Pool: $pool_name" echo " Configuración: $atime_setting ($description)" echo " Aplicado a: pool y todos los datasets existentes" echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 Note: New datasets will automatically inherit this configuration" else echo "💡 Nota: Los nuevos datasets heredarán esta configuración automáticamente" fi } # Función para configurar dispositivos de cache (L2ARC y SLOG) setup_cache_devices() { local pool_name="$1" show_title "Configuración de Dispositivos de Cache para '$pool_name'" echo "" echo "⚠️ ADVERTENCIA CRÍTICA SOBRE DISPOSITIVOS DE CACHE:" echo " 🔥 SOLO USAR SSD O NVME - NUNCA DISCOS MECÁNICOS" echo " 🚫 Los discos mecánicos (HDD) como cache EMPEORARÁN el rendimiento" echo " ✅ Los dispositivos de cache deben ser MÁS RÁPIDOS que el almacenamiento principal" echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 CACHE DEVICES INFORMATION:" else echo "💡 INFORMACIÓN SOBRE DISPOSITIVOS DE CACHE:" fi echo "" echo "🚀 L2ARC (Level 2 Adaptive Replacement Cache):" echo " • Cache de segundo nivel para lecturas frecuentes" echo " • Ideal: SSD rápido (NVMe > SATA SSD > USB 3.0)" echo " • Mejora rendimiento de lectura en datasets accedidos frecuentemente" echo " • No es crítico - si falla, el pool sigue funcionando" echo "" echo "📝 SLOG (Separate Intent Log):" echo " • Log de transacciones para escrituras síncronas" echo " • Ideal: SSD con baja latencia y alta resistencia (NVMe con power-loss protection)" echo " • Mejora rendimiento de escrituras síncronas (bases de datos, VMs)" echo " • Crítico para integridad - usar dispositivos confiables" echo "" echo "⚠️ RECOMENDACIÓN IMPORTANTE:" echo " 🔥 USAR DISPOSITIVOS NVME ES ALTAMENTE RECOMENDADO PARA CACHE" echo " • NVMe ofrece la latencia más baja y mayor throughput" echo " • Usar dispositivos lentos (USB, HDD) como cache puede EMPEORAR el rendimiento" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " • If you don't have NVMe available, consider skipping cache configuration" else echo " • Si no tienes NVMe disponible, considera omitir la configuración de cache" fi echo "" # Detectar dispositivos disponibles con clasificación por tipo local pool_devices=$(zpool status "$pool_name" 2>/dev/null | grep -E "^\s+[a-z]" | awk '{print $1}' | grep -v "raidz\|mirror\|spare\|log\|cache\|replacing") local available_cache_devices=() local nvme_devices=() local ssd_devices=() local other_devices=() # Obtener lista de todos los discos disponibles y clasificarlos local available_cache_devices=() local nvme_devices=() local ssd_devices=() local other_devices=() local in_use_devices=() while IFS= read -r disk; do local disk_name=$(basename "$disk") # Excluir dispositivos del sistema y de arranque if [[ "$disk_name" == "mmcblk0"* ]] || [[ "$disk_name" == *"boot"* ]]; then continue fi # Verificar que el dispositivo tenga tamaño válido local size_raw=$(lsblk -dpno SIZE "/dev/$disk_name" 2>/dev/null | tr -d ' ') if [[ "$size_raw" == "0B" ]] || [[ -z "$size_raw" ]]; then continue fi # Verificar que no sea el disco del sistema operativo local mount_check=$(lsblk -no MOUNTPOINT "/dev/$disk_name" 2>/dev/null | grep -E "^/$|^/boot|^/home") if [ -n "$mount_check" ]; then continue fi # Excluir discos ya en uso por el pool principal if echo "$pool_devices" | grep -q "^${disk_name}$"; then continue fi # Verificar estado de uso del dispositivo local raid_status=$(check_disk_raid_status "$disk_name") local usage_status="" if [ -n "$raid_status" ]; then # Dispositivo en uso - agregar a lista de dispositivos en uso usage_status="EN_USO" in_use_devices+=("$disk_name:$raid_status") else # Dispositivo disponible usage_status="DISPONIBLE" fi # Agregar a la lista general y clasificar por tipo available_cache_devices+=("$disk_name:$usage_status") if [[ "$disk_name" == nvme* ]]; then nvme_devices+=("$disk_name:$usage_status") elif lsblk -dpno ROTA "/dev/$disk_name" 2>/dev/null | grep -q "0"; then # ROTA=0 significa SSD ssd_devices+=("$disk_name:$usage_status") else other_devices+=("$disk_name:$usage_status") fi done < <(lsblk -dpno NAME | grep -v "^/dev/loop") # Mostrar dispositivos disponibles clasificados por tipo echo "📀 DISPOSITIVOS PARA CACHE:" echo "" if [ ${#nvme_devices[@]} -gt 0 ]; then echo "🚀 DISPOSITIVOS NVME (RECOMENDADOS):" for device_info in "${nvme_devices[@]}"; do local device_name=$(echo "$device_info" | cut -d: -f1) local usage_status=$(echo "$device_info" | cut -d: -f2) local size=$(lsblk -dpno SIZE "/dev/$device_name" 2>/dev/null | tr -d ' ') local model=$(lsblk -dpno MODEL "/dev/$device_name" 2>/dev/null | tr -d ' ') if [ "$usage_status" = "DISPONIBLE" ]; then echo " ✅ $device_name - $size - $model" else echo " ⚠️ $device_name - $size - $model (EN USO: $usage_status - se puede limpiar)" fi done echo "" fi if [ ${#ssd_devices[@]} -gt 0 ]; then echo "💾 DISPOSITIVOS SSD (ACEPTABLES):" for device_info in "${ssd_devices[@]}"; do local device_name=$(echo "$device_info" | cut -d: -f1) local usage_status=$(echo "$device_info" | cut -d: -f2) local size=$(lsblk -dpno SIZE "/dev/$device_name" 2>/dev/null | tr -d ' ') local model=$(lsblk -dpno MODEL "/dev/$device_name" 2>/dev/null | tr -d ' ') if [ "$usage_status" = "DISPONIBLE" ]; then echo " ⚠️ $device_name - $size - $model" else echo " ⚠️ $device_name - $size - $model (EN USO: $usage_status - se puede limpiar)" fi done echo "" fi if [ ${#other_devices[@]} -gt 0 ]; then echo "🐌 OTROS DISPOSITIVOS (NO RECOMENDADOS PARA CACHE):" for device_info in "${other_devices[@]}"; do local device_name=$(echo "$device_info" | cut -d: -f1) local usage_status=$(echo "$device_info" | cut -d: -f2) local size=$(lsblk -dpno SIZE "/dev/$device_name" 2>/dev/null | tr -d ' ') local model=$(lsblk -dpno MODEL "/dev/$device_name" 2>/dev/null | tr -d ' ') if [ "$usage_status" = "DISPONIBLE" ]; then echo " ❌ $device_name - $size - $model" else echo " ❌ $device_name - $size - $model (EN USO: $usage_status - se puede limpiar)" fi done echo "" fi # Mostrar información sobre dispositivos en uso if [ ${#in_use_devices[@]} -gt 0 ]; then if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 DEVICES IN USE INFORMATION:" else echo "💡 INFORMACIÓN SOBRE DISPOSITIVOS EN USO:" fi echo " • Los dispositivos marcados como 'EN USO' pueden ser limpiados y reutilizados" echo " • ⚠️ ADVERTENCIA: Limpiar un dispositivo DESTRUIRÁ todos los datos" echo " • Se te pedirá confirmación antes de proceder con dispositivos en uso" echo "" fi # Verificar dispositivos disponibles y mostrar recomendación apropiada if [ ${#nvme_devices[@]} -eq 0 ] && [ ${#ssd_devices[@]} -eq 0 ]; then # No hay NVMe ni SSD - advertencia crítica echo "⚠️ ADVERTENCIA CRÍTICA:" echo " 🔥 NO SE DETECTARON DISPOSITIVOS NVMe O SSD ADECUADOS" echo " • Los dispositivos de cache deben ser más rápidos que el almacenamiento principal" echo " • Usar dispositivos lentos como cache puede REDUCIR el rendimiento del sistema" echo " • Se recomienda FUERTEMENTE conseguir un dispositivo NVMe o SSD para cache" echo "" if ! confirm "¿Deseas continuar sin dispositivos rápidos (MUY NO RECOMENDADO)?"; then show_message "Configuración de cache cancelada - se recomienda conseguir un dispositivo NVMe o SSD" return 0 fi echo "" elif [ ${#nvme_devices[@]} -eq 0 ] && [ ${#ssd_devices[@]} -gt 0 ]; then # Solo hay SSD, no NVMe - recomendación suave if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 RECOMMENDATION:" else echo "💡 RECOMENDACIÓN:" fi echo " ✅ Se detectaron dispositivos SSD (aceptables para cache)" echo " 🚀 Para rendimiento óptimo, considera usar NVMe en el futuro" echo " 📊 Los SSD son adecuados para la mayoría de casos de uso" echo "" fi if [ ${#available_cache_devices[@]} -eq 0 ]; then show_warning "No se encontraron dispositivos disponibles para cache" show_message "Todos los dispositivos están en uso o no son adecuados" return 0 fi if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "️ CONFIGURATION OPTIONS:" else echo "️ OPCIONES DE CONFIGURACIÓN:" fi if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 1. Configure only L2ARC (read cache)" echo " 2. Configure only SLOG (write log)" echo " 3. Configure both L2ARC and SLOG" echo " 4. Configure partitions on one device (L2ARC + SLOG)" echo " 5. Cancel configuration" else echo " 1. Configurar solo L2ARC (cache de lectura)" echo " 2. Configurar solo SLOG (log de escritura)" echo " 3. Configurar ambos L2ARC y SLOG" echo " 4. Configurar particiones en un dispositivo (L2ARC + SLOG)" echo " 5. Cancelar configuración" fi echo "" while true; do read -p "👉 Selecciona una opción (1-5): " config_choice case $config_choice in 1) if setup_l2arc_only "$pool_name" "${available_cache_devices[@]}"; then break else echo "" echo "️ OPCIONES DE CONFIGURACIÓN:" echo " 1. Configurar solo L2ARC (cache de lectura)" echo " 2. Configurar solo SLOG (log de escritura)" echo " 3. Configurar ambos L2ARC y SLOG" echo " 4. Configurar particiones en un dispositivo (L2ARC + SLOG)" echo " 5. Cancelar configuración" echo "" fi ;; 2) if setup_slog_only "$pool_name" "${available_cache_devices[@]}"; then break else echo "" echo "️ OPCIONES DE CONFIGURACIÓN:" echo " 1. Configurar solo L2ARC (cache de lectura)" echo " 2. Configurar solo SLOG (log de escritura)" echo " 3. Configurar ambos L2ARC y SLOG" echo " 4. Configurar particiones en un dispositivo (L2ARC + SLOG)" echo " 5. Cancelar configuración" echo "" fi ;; 3) if setup_l2arc_and_slog_separate "$pool_name" "${available_cache_devices[@]}"; then break else echo "" echo "️ OPCIONES DE CONFIGURACIÓN:" echo " 1. Configurar solo L2ARC (cache de lectura)" echo " 2. Configurar solo SLOG (log de escritura)" echo " 3. Configurar ambos L2ARC y SLOG" echo " 4. Configurar particiones en un dispositivo (L2ARC + SLOG)" echo " 5. Cancelar configuración" echo "" fi ;; 4) if setup_partitioned_cache "$pool_name" "${available_cache_devices[@]}"; then break else echo "" echo "️ OPCIONES DE CONFIGURACIÓN:" echo " 1. Configurar solo L2ARC (cache de lectura)" echo " 2. Configurar solo SLOG (log de escritura)" echo " 3. Configurar ambos L2ARC y SLOG" echo " 4. Configurar particiones en un dispositivo (L2ARC + SLOG)" echo " 5. Cancelar configuración" echo "" fi ;; 5) show_message "Configuración de cache cancelada" return 0 ;; *) echo "❌ Opción inválida. Selecciona 1, 2, 3, 4 o 5." echo "" echo "️ OPCIONES DE CONFIGURACIÓN:" echo " 1. Configurar solo L2ARC (cache de lectura)" echo " 2. Configurar solo SLOG (log de escritura)" echo " 3. Configurar ambos L2ARC y SLOG" echo " 4. Configurar particiones en un dispositivo (L2ARC + SLOG)" echo " 5. Cancelar configuración" echo "" ;; esac done } # Función para configurar solo L2ARC setup_l2arc_only() { local pool_name="$1" shift local available_devices=("$@") show_title "Configuración de L2ARC para '$pool_name'" echo "" echo "📖 Selecciona el dispositivo para L2ARC (cache de lectura):" for i in "${!available_devices[@]}"; do local device_info="${available_devices[$i]}" local device_name=$(echo "$device_info" | cut -d: -f1) local usage_status=$(echo "$device_info" | cut -d: -f2) local size=$(lsblk -dpno SIZE "/dev/$device_name" | tr -d ' ') local model=$(lsblk -dpno MODEL "/dev/$device_name" | tr -d ' ') if [ "$usage_status" = "DISPONIBLE" ]; then echo " $((i+1)). $device_name - $size - $model" else echo " $((i+1)). $device_name - $size - $model (EN USO: $usage_status)" fi done while true; do read -p "👉 Selecciona dispositivo (1-${#available_devices[@]}): " device_choice if [[ "$device_choice" =~ ^[0-9]+$ ]] && [ "$device_choice" -ge 1 ] && [ "$device_choice" -le ${#available_devices[@]} ]; then local device_info="${available_devices[$((device_choice-1))]}" local selected_device=$(echo "$device_info" | cut -d: -f1) local usage_status=$(echo "$device_info" | cut -d: -f2) # Verificar si el dispositivo está en uso if [ "$usage_status" != "DISPONIBLE" ]; then echo "" echo "⚠️ ADVERTENCIA: El dispositivo $selected_device está EN USO" echo " 📁 Uso actual: $usage_status" echo " 💀 Limpiar este dispositivo DESTRUIRÁ todos los datos" echo "" if ! confirm "¿Estás SEGURO de que quieres limpiar y usar $selected_device para L2ARC?"; then echo "Selecciona otro dispositivo..." continue fi fi # Verificar si es un disco mecánico if ! lsblk -dpno ROTA "/dev/$selected_device" 2>/dev/null | grep -q "0"; then echo "" echo "🚨 ADVERTENCIA: El dispositivo $selected_device parece ser un DISCO MECÁNICO" echo " ❌ Los discos mecánicos NO deben usarse como cache" echo " 📉 Esto EMPEORARÁ significativamente el rendimiento del sistema" echo " ✅ Usa solo SSD o NVMe para cache" echo "" if ! confirm "¿Estás SEGURO de que quieres usar este disco mecánico? (MUY NO RECOMENDADO)"; then echo "Selecciona otro dispositivo..." continue fi echo "" if ! confirm "⚠️ ÚLTIMA ADVERTENCIA: ¿Realmente quieres proceder con un disco mecánico?"; then echo "Selecciona otro dispositivo..." continue fi fi break else echo "❌ Selección inválida. Usa números del 1 al ${#available_devices[@]}." fi done # Confirmar y aplicar echo "" show_warning "⚠️ Esto limpiará completamente el dispositivo /dev/$selected_device" if confirm "¿Continuar con la configuración de L2ARC en /dev/$selected_device?"; then show_message "Preparando dispositivo /dev/$selected_device para L2ARC..." # Limpiar dispositivo clean_disk "$selected_device" # Agregar como L2ARC show_message "Agregando /dev/$selected_device como L2ARC al pool '$pool_name'..." if sudo zpool add "$pool_name" cache "/dev/$selected_device"; then show_message "✅ L2ARC configurado exitosamente" echo "" show_message "📊 CONFIGURACIÓN L2ARC COMPLETADA:" echo " Pool: $pool_name" echo " Dispositivo L2ARC: /dev/$selected_device" echo " Función: Cache de lectura (mejora acceso a datos frecuentes)" echo " 💡 Cache devices no requieren compatibilidad de ashift" return 0 else show_error "❌ Error configurando L2ARC" return 1 fi else show_message "Configuración de L2ARC cancelada" return 1 fi } # Función para configurar solo SLOG setup_slog_only() { local pool_name="$1" shift local available_devices=("$@") show_title "Configuración de SLOG para '$pool_name'" echo "" echo "⚠️ IMPORTANTE - REQUISITOS PARA SLOG:" echo " • Usar SSD de alta calidad con protección contra cortes de energía" echo " • Evitar dispositivos USB o SD para SLOG" echo " • Preferir NVMe > SATA SSD > otros" echo "" echo "📖 Selecciona el dispositivo para SLOG (log de escritura):" for i in "${!available_devices[@]}"; do local device_info="${available_devices[$i]}" local device_name=$(echo "$device_info" | cut -d: -f1) local usage_status=$(echo "$device_info" | cut -d: -f2) local size=$(lsblk -dpno SIZE "/dev/$device_name" | tr -d ' ') local model=$(lsblk -dpno MODEL "/dev/$device_name" | tr -d ' ') local recommendation="" if [[ "$device_name" == nvme* ]]; then recommendation=" ⭐ RECOMENDADO" elif lsblk -dpno ROTA "/dev/$device_name" | grep -q "0"; then recommendation=" ✅ ADECUADO" else recommendation=" ⚠️ NO RECOMENDADO (HDD)" fi if [ "$usage_status" = "DISPONIBLE" ]; then echo " $((i+1)). $device_name - $size - $model$recommendation" else echo " $((i+1)). $device_name - $size - $model$recommendation (EN USO: $usage_status)" fi done while true; do read -p "👉 Selecciona dispositivo (1-${#available_devices[@]}): " device_choice if [[ "$device_choice" =~ ^[0-9]+$ ]] && [ "$device_choice" -ge 1 ] && [ "$device_choice" -le ${#available_devices[@]} ]; then local device_info="${available_devices[$((device_choice-1))]}" local selected_device=$(echo "$device_info" | cut -d: -f1) local usage_status=$(echo "$device_info" | cut -d: -f2) # Verificar si el dispositivo está en uso if [ "$usage_status" != "DISPONIBLE" ]; then echo "" echo "⚠️ ADVERTENCIA CRÍTICA: El dispositivo $selected_device está EN USO" echo " 📁 Uso actual: $usage_status" echo " 💀 Limpiar este dispositivo DESTRUIRÁ todos los datos" echo " 🔧 SLOG es crítico - asegúrate de que puedes perder estos datos" echo "" if ! confirm "¿Estás SEGURO de que quieres limpiar y usar $selected_device para SLOG?"; then echo "Selecciona otro dispositivo..." continue fi fi break else echo "❌ Selección inválida. Usa números del 1 al ${#available_devices[@]}." fi done # Verificar si es adecuado para SLOG if ! [[ "$selected_device" == nvme* ]] && lsblk -dpno ROTA "/dev/$selected_device" | grep -q "1"; then echo "" echo "🚨 ADVERTENCIA CRÍTICA: El dispositivo $selected_device es un DISCO MECÁNICO" echo " ❌ Los discos mecánicos NO deben usarse como SLOG" echo " 💀 Esto puede causar PÉRDIDA DE DATOS y rendimiento EXTREMADAMENTE pobre" echo " 🔧 SLOG requiere dispositivos de baja latencia (SSD/NVMe)" echo " ⚡ Un SLOG lento puede bloquear TODAS las escrituras del sistema" echo "" if ! confirm "¿Estás SEGURO de que quieres usar este disco mecánico para SLOG? (PELIGROSO)"; then show_message "Configuración cancelada por el usuario" return 0 fi echo "" if ! confirm "⚠️ ÚLTIMA ADVERTENCIA: ¿Realmente quieres proceder? (PUEDE CAUSAR PROBLEMAS GRAVES)"; then show_message "Configuración cancelada por el usuario" return 0 fi fi # Confirmar y aplicar echo "" show_warning "⚠️ Esto limpiará completamente el dispositivo /dev/$selected_device" if confirm "¿Continuar con la configuración de SLOG en /dev/$selected_device?"; then show_message "Preparando dispositivo /dev/$selected_device para SLOG..." # Limpiar dispositivo clean_disk "$selected_device" # Agregar como SLOG show_message "Agregando /dev/$selected_device como SLOG al pool '$pool_name'..." if sudo zpool add "$pool_name" log "/dev/$selected_device"; then show_message "✅ SLOG configurado exitosamente" echo "" show_message "📊 CONFIGURACIÓN SLOG COMPLETADA:" echo " Pool: $pool_name" echo " Dispositivo SLOG: /dev/$selected_device" echo " Función: Log de escritura (mejora escrituras síncronas)" echo " 💡 Cache devices no requieren compatibilidad de ashift" return 0 else show_error "❌ Error configurando SLOG" return 1 fi else show_message "Configuración de SLOG cancelada" return 1 fi } # Función para configurar L2ARC y SLOG en dispositivos separados setup_l2arc_and_slog_separate() { local pool_name="$1" shift local available_devices=("$@") if [ ${#available_devices[@]} -lt 2 ]; then show_error "Se necesitan al menos 2 dispositivos disponibles para configurar L2ARC y SLOG por separado" return 1 fi show_title "Configuración de L2ARC y SLOG Separados para '$pool_name'" echo "" echo "📖 Selecciona dispositivos para L2ARC y SLOG:" echo "" # Mostrar dispositivos con recomendaciones for i in "${!available_devices[@]}"; do local device_info="${available_devices[$i]}" local device_name=$(echo "$device_info" | cut -d: -f1) local usage_status=$(echo "$device_info" | cut -d: -f2) local size=$(lsblk -dpno SIZE "/dev/$device_name" | tr -d ' ') local model=$(lsblk -dpno MODEL "/dev/$device_name" | tr -d ' ') local recommendation="" if [[ "$device_name" == nvme* ]]; then recommendation=" ⭐ EXCELENTE para SLOG, BUENO para L2ARC" elif lsblk -dpno ROTA "/dev/$device_name" | grep -q "0"; then recommendation=" ✅ BUENO para L2ARC, REGULAR para SLOG" else recommendation=" ⚠️ SOLO para L2ARC (NO para SLOG)" fi if [ "$usage_status" = "DISPONIBLE" ]; then echo " $((i+1)). $device_name - $size - $model$recommendation" else echo " $((i+1)). $device_name - $size - $model$recommendation (EN USO: $usage_status)" fi done # Seleccionar dispositivo para L2ARC echo "" echo "🚀 SELECCIÓN DE L2ARC:" while true; do read -p "👉 Dispositivo para L2ARC (1-${#available_devices[@]}): " l2arc_choice if [[ "$l2arc_choice" =~ ^[0-9]+$ ]] && [ "$l2arc_choice" -ge 1 ] && [ "$l2arc_choice" -le ${#available_devices[@]} ]; then local device_info="${available_devices[$((l2arc_choice-1))]}" local l2arc_device=$(echo "$device_info" | cut -d: -f1) local l2arc_usage=$(echo "$device_info" | cut -d: -f2) # Verificar si el dispositivo L2ARC está en uso if [ "$l2arc_usage" != "DISPONIBLE" ]; then echo "" echo "⚠️ ADVERTENCIA: El dispositivo $l2arc_device está EN USO" echo " 📁 Uso actual: $l2arc_usage" echo " 💀 Limpiar este dispositivo DESTRUIRÁ todos los datos" echo "" if ! confirm "¿Continuar con $l2arc_device para L2ARC?"; then echo "Selecciona otro dispositivo..." continue fi fi break else echo "❌ Selección inválida. Usa números del 1 al ${#available_devices[@]}." fi done # Seleccionar dispositivo para SLOG (excluyendo el ya seleccionado) echo "" echo "📝 SELECCIÓN DE SLOG:" echo "Dispositivos disponibles (excluyendo $l2arc_device):" local slog_devices=() for i in "${!available_devices[@]}"; do local device_info="${available_devices[$i]}" local device_name=$(echo "$device_info" | cut -d: -f1) local usage_status=$(echo "$device_info" | cut -d: -f2) if [ "$device_name" != "$l2arc_device" ]; then slog_devices+=("$device_info") local size=$(lsblk -dpno SIZE "/dev/$device_name" | tr -d ' ') local model=$(lsblk -dpno MODEL "/dev/$device_name" | tr -d ' ') local recommendation="" if [[ "$device_name" == nvme* ]]; then recommendation=" ⭐ RECOMENDADO" elif lsblk -dpno ROTA "/dev/$device_name" | grep -q "0"; then recommendation=" ✅ ADECUADO" else recommendation=" ⚠️ NO RECOMENDADO" fi if [ "$usage_status" = "DISPONIBLE" ]; then echo " ${#slog_devices[@]}. $device_name - $size - $model$recommendation" else echo " ${#slog_devices[@]}. $device_name - $size - $model$recommendation (EN USO: $usage_status)" fi fi done while true; do read -p "👉 Dispositivo para SLOG (1-${#slog_devices[@]}): " slog_choice if [[ "$slog_choice" =~ ^[0-9]+$ ]] && [ "$slog_choice" -ge 1 ] && [ "$slog_choice" -le ${#slog_devices[@]} ]; then local device_info="${slog_devices[$((slog_choice-1))]}" local slog_device=$(echo "$device_info" | cut -d: -f1) local slog_usage=$(echo "$device_info" | cut -d: -f2) # Verificar si el dispositivo SLOG está en uso if [ "$slog_usage" != "DISPONIBLE" ]; then echo "" echo "⚠️ ADVERTENCIA CRÍTICA: El dispositivo $slog_device está EN USO" echo " 📁 Uso actual: $slog_usage" echo " 💀 Limpiar este dispositivo DESTRUIRÁ todos los datos" echo " 🔧 SLOG es crítico para integridad de datos" echo "" if ! confirm "¿Continuar con $slog_device para SLOG?"; then echo "Selecciona otro dispositivo..." continue fi fi break else echo "❌ Selección inválida. Usa números del 1 al ${#slog_devices[@]}." fi done # Verificar SLOG si es HDD if ! [[ "$slog_device" == nvme* ]] && lsblk -dpno ROTA "/dev/$slog_device" | grep -q "1"; then show_warning "⚠️ ADVERTENCIA: Dispositivo SLOG seleccionado es HDD" if ! confirm "¿Continuar con HDD para SLOG? (no recomendado)"; then show_message "Configuración cancelada" return 0 fi fi # Mostrar resumen y confirmar echo "" show_message "📊 RESUMEN DE CONFIGURACIÓN:" echo " Pool: $pool_name" echo " L2ARC: /dev/$l2arc_device (cache de lectura)" echo " SLOG: /dev/$slog_device (log de escritura)" echo "" show_warning "⚠️ Esto limpiará completamente ambos dispositivos" if confirm "¿Continuar con la configuración?"; then # Configurar L2ARC show_message "Preparando /dev/$l2arc_device para L2ARC..." clean_disk "$l2arc_device" # Verificar compatibilidad de tamaño de sector para L2ARC show_message "Verificando compatibilidad de sector para L2ARC..." local pool_sector_size=$(zpool get ashift "$pool_name" -H -o value 2>/dev/null | head -n 1) local l2arc_sector_size=$(sudo blockdev --getpbsz "/dev/$l2arc_device" 2>/dev/null) local l2arc_force="" if [ -n "$pool_sector_size" ] && [ -n "$l2arc_sector_size" ]; then local pool_bytes=$((2 ** pool_sector_size)) if [ "$l2arc_sector_size" -ne "$pool_bytes" ]; then echo " ⚠️ Pool: ${pool_bytes} bytes | L2ARC: ${l2arc_sector_size} bytes" if confirm "¿Forzar L2ARC?"; then l2arc_force="-f" else show_message "Configuración cancelada" return 1 fi fi fi show_message "Agregando L2ARC al pool..." if ! sudo zpool add $l2arc_force "$pool_name" cache "/dev/$l2arc_device"; then show_error "❌ Error configurando L2ARC" return 1 fi # Configurar SLOG show_message "Preparando /dev/$slog_device para SLOG..." clean_disk "$slog_device" # Verificar compatibilidad de tamaño de sector para SLOG show_message "Verificando compatibilidad de sector para SLOG..." local slog_sector_size=$(sudo blockdev --getpbsz "/dev/$slog_device" 2>/dev/null) local slog_force="" if [ -n "$pool_sector_size" ] && [ -n "$slog_sector_size" ]; then local pool_bytes=$((2 ** pool_sector_size)) if [ "$slog_sector_size" -ne "$pool_bytes" ]; then echo " ⚠️ Pool: ${pool_bytes} bytes | SLOG: ${slog_sector_size} bytes" if confirm "¿Forzar SLOG? (RIESGOSO para integridad)"; then slog_force="-f" else show_message "Configuración cancelada" return 1 fi fi fi show_message "Agregando SLOG al pool..." if ! sudo zpool add $slog_force "$pool_name" log "/dev/$slog_device"; then show_error "❌ Error configurando SLOG" return 1 fi show_message "✅ L2ARC y SLOG configurados exitosamente" echo "" show_message "📊 CONFIGURACIÓN COMPLETADA:" echo " Pool: $pool_name" echo " L2ARC: /dev/$l2arc_device (mejora lecturas frecuentes)" echo " SLOG: /dev/$slog_device (mejora escrituras síncronas)" return 0 else show_message "Configuración cancelada" return 1 fi } # Función para configurar particiones L2ARC + SLOG en un dispositivo setup_partitioned_cache() { local pool_name="$1" shift local available_devices=("$@") show_title "Configuración de L2ARC + SLOG Particionado para '$pool_name'" echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 PARTITION CONFIGURATION:" else echo "💡 CONFIGURACIÓN PARTICIONADA:" fi echo " Esta opción crea particiones en un solo dispositivo:" echo " • 80% del espacio para L2ARC (cache de lectura)" echo " • 20% del espacio para SLOG (log de escritura)" echo "" echo "⚠️ RECOMENDACIONES:" echo " • Usar solo con SSD de alta calidad (preferiblemente NVMe)" echo " • El dispositivo debe tener al menos 8GB de espacio" echo " • No recomendado para HDD o dispositivos USB" echo "" # Mostrar dispositivos recomendados echo "💾 DISPOSITIVOS DISPONIBLES:" for i in "${!available_devices[@]}"; do local device_info="${available_devices[$i]}" local device_name=$(echo "$device_info" | cut -d: -f1) local usage_status=$(echo "$device_info" | cut -d: -f2) local size=$(lsblk -dpno SIZE "/dev/$device_name" | tr -d ' ') local size_bytes=$(lsblk -dpno SIZE "/dev/$device_name" --bytes) local model=$(lsblk -dpno MODEL "/dev/$device_name" | tr -d ' ') local recommendation="" if [[ "$device_name" == nvme* ]]; then recommendation=" ⭐ EXCELENTE" elif lsblk -dpno ROTA "/dev/$device_name" | grep -q "0"; then if [ "$size_bytes" -gt 8589934592 ]; then # 8GB recommendation=" ✅ ADECUADO" else recommendation=" ⚠️ PEQUEÑO (mín 8GB recomendado)" fi else recommendation=" ❌ NO RECOMENDADO (HDD)" fi if [ "$usage_status" = "DISPONIBLE" ]; then echo " $((i+1)). $device_name - $size - $model$recommendation" else echo " $((i+1)). $device_name - $size - $model$recommendation (EN USO: $usage_status)" fi done while true; do read -p "👉 Selecciona dispositivo (1-${#available_devices[@]}): " device_choice if [[ "$device_choice" =~ ^[0-9]+$ ]] && [ "$device_choice" -ge 1 ] && [ "$device_choice" -le ${#available_devices[@]} ]; then local device_info="${available_devices[$((device_choice-1))]}" local selected_device=$(echo "$device_info" | cut -d: -f1) local usage_status=$(echo "$device_info" | cut -d: -f2) # Verificar si el dispositivo está en uso if [ "$usage_status" != "DISPONIBLE" ]; then echo "" echo "⚠️ ADVERTENCIA: El dispositivo $selected_device está EN USO" echo " 📁 Uso actual: $usage_status" echo " 💀 Crear particiones DESTRUIRÁ todos los datos" echo "" if ! confirm "¿Estás SEGURO de que quieres limpiar y particionar $selected_device?"; then echo "Selecciona otro dispositivo..." continue fi fi break else echo "❌ Selección inválida. Usa números del 1 al ${#available_devices[@]}." fi done # Verificar si el dispositivo es adecuado local size_bytes=$(lsblk -dpno SIZE "/dev/$selected_device" --bytes) if [ "$size_bytes" -lt 8589934592 ]; then # 8GB show_warning "⚠️ El dispositivo es menor a 8GB, puede no ser óptimo" if ! confirm "¿Continuar de todas formas?"; then show_message "Configuración cancelada" return 0 fi fi if ! [[ "$selected_device" == nvme* ]] && lsblk -dpno ROTA "/dev/$selected_device" | grep -q "1"; then show_warning "⚠️ ADVERTENCIA: El dispositivo seleccionado es un HDD" show_warning " No es recomendado usar HDD para particionado L2ARC+SLOG" if ! confirm "¿Estás seguro de continuar?"; then show_message "Configuración cancelada" return 0 fi fi # Mostrar resumen local size_gb=$(( size_bytes / 1024 / 1024 / 1024 )) local l2arc_size=$(( size_gb * 80 / 100 )) local slog_size=$(( size_gb * 20 / 100 )) echo "" show_message "📊 CONFIGURACIÓN PLANIFICADA:" echo " Dispositivo: /dev/$selected_device ($size_gb GB)" echo " Partición L2ARC: ~${l2arc_size}GB (80%)" echo " Partición SLOG: ~${slog_size}GB (20%)" echo "" show_warning "⚠️ Esto destruirá todos los datos en /dev/$selected_device" if confirm "¿Continuar con el particionado?"; then echo "" show_message "🔍 VERIFICACIÓN PREVIA DEL POOL:" echo " Pool: $pool_name" echo " Estado: $(zpool get health "$pool_name" -H -o value 2>/dev/null || echo "DESCONOCIDO")" echo " Versión: $(zpool get version "$pool_name" -H -o value 2>/dev/null || echo "DESCONOCIDA")" echo "" show_message "Preparando dispositivo /dev/$selected_device..." # Limpiar dispositivo if ! clean_disk "$selected_device"; then show_error "❌ Error limpiando el dispositivo" show_message "Regresando al menú anterior..." return 1 fi # Crear tabla de particiones GPT show_message "Creando tabla de particiones..." if ! sudo parted "/dev/$selected_device" --script mklabel gpt; then show_error "❌ Error creando tabla de particiones" show_message "Regresando al menú anterior..." return 1 fi # Crear partición L2ARC (80%) show_message "Creando partición L2ARC (80% del espacio)..." if ! sudo parted "/dev/$selected_device" --script mkpart l2arc ext4 1MiB 80%; then show_error "❌ Error creando partición L2ARC" show_message "Regresando al menú anterior..." return 1 fi # Crear partición SLOG (20%) show_message "Creando partición SLOG (20% del espacio)..." if ! sudo parted "/dev/$selected_device" --script mkpart slog ext4 80% 100%; then show_error "❌ Error creando partición SLOG" show_message "Regresando al menú anterior..." return 1 fi # Esperar que el kernel reconozca las particiones sudo partprobe "/dev/$selected_device" sleep 3 # Determinar nombres de particiones local l2arc_partition="${selected_device}1" local slog_partition="${selected_device}2" # Si es NVMe, las particiones tienen formato diferente if [[ "$selected_device" == nvme* ]]; then l2arc_partition="${selected_device}p1" slog_partition="${selected_device}p2" fi # Verificar que las particiones existen if [ ! -b "/dev/$l2arc_partition" ] || [ ! -b "/dev/$slog_partition" ]; then show_error "❌ Error: Las particiones no se crearon correctamente" show_message "Regresando al menú anterior..." return 1 fi # Agregar L2ARC y SLOG echo "" show_message "🔧 Agregando L2ARC y SLOG al pool en un solo comando..." echo " 💡 Cache devices no requieren compatibilidad de ashift" echo " Comando: zpool add '$pool_name' cache '/dev/$l2arc_partition' log '/dev/$slog_partition'" # Verificar que ambas particiones existen y son accesibles if [ ! -b "/dev/$l2arc_partition" ]; then show_error "❌ Partición L2ARC /dev/$l2arc_partition no existe" return 1 fi if [ -n "$pool_sector_size" ] && [ -n "$cache_sector_size" ]; then local pool_bytes=$((2 ** pool_sector_size)) echo " Pool sector size: ${pool_bytes} bytes (ashift=$pool_sector_size)" echo " Cache sector size: ${cache_sector_size} bytes" if [ "$cache_sector_size" -ne "$pool_bytes" ]; then echo "" show_error "⚠️ INCOMPATIBILIDAD DE TAMAÑO DE SECTOR:" echo " 📊 Pool '$pool_name': ${pool_bytes} bytes" echo " 💾 Cache device: ${cache_sector_size} bytes" echo "" show_message "[INFO] 🔍 CAUSA DEL PROBLEMA:" echo " El pool fue creado con ashift=$pool_sector_size" if [ "$pool_sector_size" -eq 0 ]; then echo " ⚠️ ashift=0 indica pool creado sin configuración óptima" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo " 💡 For new pools, the script now uses automatic ashift" else echo " 💡 Para nuevos pools, el script ahora usa ashift automático" fi echo " � Recomendación: Recrear pool con ashift=12 para compatibilidad" fi echo "" echo "�💡 OPCIONES DISPONIBLES:" echo " 1. Continuar SIN cache (recomendado)" echo " 2. Forzar adición con -f (puede causar problemas)" echo " 3. Usar un dispositivo con sector compatible" echo " 4. Recrear pool con ashift correcto (óptimo a largo plazo)" echo "" read -p "👉 Selecciona opción (1-3): " sector_choice case $sector_choice in 1) show_message "Configuración cancelada - pool funcionará sin cache" return 1 ;; 2) show_warning "⚠️ Usando -f para forzar la adición..." local force_flag="-f" ;; 3) show_message "Configuración cancelada - selecciona otro dispositivo" return 1 ;; *) show_message "Opción inválida - cancelando configuración" return 1 ;; esac else echo " ✅ Tamaños de sector compatibles" local force_flag="" fi else echo " ⚠️ No se pudo verificar tamaño de sector completamente" echo " Intentando adición normal..." local force_flag="" fi # Agregar L2ARC echo "" show_message "🔧 Agregando L2ARC y SLOG al pool en un solo comando..." echo " Comando: zpool add $force_flag '$pool_name' cache '/dev/$l2arc_partition' log '/dev/$slog_partition'" # Verificar que ambas particiones existen y son accesibles if [ ! -b "/dev/$l2arc_partition" ]; then show_error "❌ Partición L2ARC /dev/$l2arc_partition no existe" return 1 fi if [ ! -b "/dev/$slog_partition" ]; then show_error "❌ Partición SLOG /dev/$slog_partition no existe" return 1 fi # Mostrar información de ambas particiones antes de agregar echo " Partición L2ARC: $(lsblk -no SIZE,FSTYPE "/dev/$l2arc_partition" 2>/dev/null || echo "sin formato")" echo " Partición SLOG: $(lsblk -no SIZE,FSTYPE "/dev/$slog_partition" 2>/dev/null || echo "sin formato")" if sudo zpool add "$pool_name" cache "/dev/$l2arc_partition" log "/dev/$slog_partition" 2>&1; then show_message "✅ L2ARC y SLOG agregados exitosamente en un solo comando" echo "" show_message "📊 CONFIGURACIÓN COMPLETADA:" echo " Pool: $pool_name" echo " Dispositivo base: /dev/$selected_device" echo " L2ARC: /dev/$l2arc_partition (~${l2arc_size}GB)" echo " SLOG: /dev/$slog_partition (~${slog_size}GB)" echo " Función: Cache de lectura + Log de escritura optimizados" echo " 💡 Ambos dispositivos agregados en comando único" return 0 else local exit_code=$? show_error "❌ Error agregando L2ARC y SLOG (código: $exit_code)" echo "" echo "� INFORMACIÓN DE DIAGNÓSTICO:" echo " • Pool status:" zpool status "$pool_name" | head -n 10 echo " • Dispositivo L2ARC:" lsblk "/dev/$l2arc_partition" 2>/dev/null || echo " No se puede leer la partición" echo " • Dispositivo SLOG:" lsblk "/dev/$slog_partition" 2>/dev/null || echo " No se puede leer la partición" echo " • Posibles causas:" echo " - Partición ya en uso" echo " - Permisos insuficientes" echo " - Pool en estado degradado" echo " 💡 Cache devices no requieren compatibilidad de ashift" show_message "Regresando al menú anterior..." return 1 fi else show_message "Configuración cancelada" return 1 fi } # Función para crear snapshots iniciales create_initial_snapshots() { if sudo zpool add $force_flag "$pool_name" log "/dev/$slog_partition" 2>&1; then show_message "✅ SLOG agregado exitosamente" else local exit_code=$? show_error "❌ Error agregando SLOG (código: $exit_code)" echo "" echo "� INFORMACIÓN DE DIAGNÓSTICO:" echo " • Pool status:" zpool status "$pool_name" | head -n 10 echo " • Dispositivo SLOG:" lsblk "/dev/$slog_partition" 2>/dev/null || echo " No se puede leer la partición" echo " • Posibles causas:" echo " - Sector size incompatible" echo " - Partición ya en uso" echo " - Permisos insuficientes" echo " - Pool en estado degradado" show_message "Regresando al menú anterior..." return 1 fi } # Función para crear snapshots iniciales create_initial_snapshots() { local pool_name="$1" show_title "Creación de Snapshots Iniciales" echo "" if [ "$SCRIPT_LANGUAGE" = "en" ]; then echo "💡 Snapshots are instant copies of datasets that take no initial space." else echo "💡 Los snapshots son copias instantáneas de datasets que no ocupan espacio inicial." fi echo " Son útiles para:" echo " • Backups rápidos antes de cambios importantes" echo " • Restauración rápida a estados anteriores" echo " • Bases para replicación de datos" echo "" # Obtener datasets del pool local datasets=$(zfs list -H -o name -r "$pool_name" 2>/dev/null) if [ -n "$datasets" ]; then echo "📁 Datasets disponibles para snapshot:" for dataset in $datasets; do echo " • $dataset" done echo "" if confirm "¿Crear snapshot inicial de todos los datasets?"; then local timestamp=$(date +"%Y%m%d-%H%M%S") local snapshot_name="inicial-$timestamp" for dataset in $datasets; do show_message "Creando snapshot: $dataset@$snapshot_name" if sudo zfs snapshot "$dataset@$snapshot_name"; then show_message "✅ Snapshot creado: $dataset@$snapshot_name" else show_error "❌ Error creando snapshot de $dataset" fi done echo "" show_message "📊 Snapshots creados:" sudo zfs list -t snapshot -o name,used,creation | grep "@$snapshot_name" fi else show_warning "No se encontraron datasets en el pool '$pool_name'" fi } # Función para mostrar estado completo del pool show_pool_status() { local pool_name="$1" # Debugging para identificar el problema if [ -z "$pool_name" ]; then show_error "Error crítico: show_pool_status llamado sin parámetro de pool_name" echo "Información de debug:" echo " Parámetros recibidos: '$@'" echo " Número de parámetros: $#" echo " Variable global POOL_NAME: '$POOL_NAME'" echo " Pools disponibles: $(zpool list -H -o name 2>/dev/null | tr '\n' ' ')" read -p "Presiona Enter para continuar..." return 1 fi show_title "Estado Completo del Pool '$pool_name'" echo "" # Estado del pool echo "🏊 ESTADO DEL POOL:" sudo zpool status "$pool_name" echo "" # Información del pool echo "📊 INFORMACIÓN DEL POOL:" sudo zpool list "$pool_name" echo "" # Datasets echo "📁 DATASETS:" sudo zfs list -r "$pool_name" echo "" # Snapshots (si existen) local snapshots=$(zfs list -t snapshot -H -o name 2>/dev/null | grep "^$pool_name") if [ -n "$snapshots" ]; then echo "📸 SNAPSHOTS:" sudo zfs list -t snapshot -r "$pool_name" echo "" fi # Propiedades del pool echo "⚙️ PROPIEDADES PRINCIPALES:" echo "ARC Max: $(cat /sys/module/zfs/parameters/zfs_arc_max 2>/dev/null || echo 'No configurado')" sudo zpool get all "$pool_name" | grep -E "ashift|feature|autoreplace|failmode" } # Ejecutar función principal main "$@"