#!/usr/bin/env bash # Repair and redeploy thin1-thin3 storage pools on pve and pve2 # This script removes broken storage configurations and recreates them properly set -euo pipefail PROXMOX_HOST_PVE="192.168.11.11" PROXMOX_HOST_PVE2="192.168.11.12" PVE_PASS="password" STORAGE_POOLS=("thin1" "thin2" "thin3") # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[✓]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } # Check what's using a storage pool check_storage_usage() { local host=$1 local storage=$2 log_info "Checking if $storage is in use on $host..." local usage=$(sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} \ "pvesm list $storage 2>/dev/null | wc -l" || echo "0") if [ "$usage" -gt 1 ]; then log_warn "Storage $storage has $((usage - 1)) items (header excluded)" return 1 else log_info "Storage $storage appears to be unused" return 0 fi } # Remove storage from Proxmox remove_storage() { local host=$1 local storage=$2 log_info "Removing storage $storage from Proxmox on $host..." # Check if storage exists if ! sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} \ "pvesm status 2>/dev/null | grep -q '$storage'" 2>/dev/null; then log_info "Storage $storage does not exist, skipping removal" return 0 fi # Remove from Proxmox config sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} \ "pvesm remove $storage 2>&1" || { log_warn "Failed to remove via pvesm, trying to edit config directly..." # Remove from storage.cfg sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} \ "sed -i '/^lvmthin: $storage$/,/^$/d' /etc/pve/storage.cfg 2>/dev/null" || true } log_success "Storage $storage removed from Proxmox configuration" } # Get volume group name for storage get_vg_for_storage() { local host=$1 local storage=$2 sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} \ "cat /etc/pve/storage.cfg 2>/dev/null | grep -A 5 'lvmthin: $storage' | grep 'vgname' | awk '{print \$2}'" 2>/dev/null || echo "" } # Get thin pool name for storage get_thinpool_for_storage() { local host=$1 local storage=$2 local pool=$(sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} \ "cat /etc/pve/storage.cfg 2>/dev/null | grep -A 5 'lvmthin: $storage' | grep 'thinpool' | awk '{print \$2}'" 2>/dev/null || echo "") # If pool is in vg/pool format, extract just the pool name if [[ "$pool" == *"/"* ]]; then pool=$(echo "$pool" | cut -d'/' -f2) fi echo "$pool" } # Check if VG exists check_vg_exists() { local host=$1 local vg_name=$2 sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} \ "vgs $vg_name 2>/dev/null | grep -q '$vg_name'" 2>/dev/null } # Check if thin pool exists in VG check_thinpool_exists() { local host=$1 local vg_name=$2 local pool_name=$3 sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} \ "lvs $vg_name/$pool_name 2>/dev/null | grep -q '$pool_name'" 2>/dev/null } # Create thin pool if it doesn't exist create_thin_pool() { local host=$1 local vg_name=$2 local pool_name=$3 log_info "Creating thin pool $vg_name/$pool_name on $host..." # Check if VG exists if ! check_vg_exists "$host" "$vg_name"; then log_error "Volume group $vg_name does not exist on $host" return 1 fi # Check if pool already exists if check_thinpool_exists "$host" "$vg_name" "$pool_name"; then log_info "Thin pool $vg_name/$pool_name already exists" return 0 fi # Get available space local vg_free=$(sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} \ "vgs -o vg_free --noheadings --units g $vg_name 2>/dev/null | awk '{print int(\$1)}'" || echo "0") if [ "$vg_free" -lt 10 ]; then log_error "Not enough free space in $vg_name (${vg_free}G available)" return 1 fi # Use 80% of available space local pool_size=$((vg_free * 80 / 100)) log_info "Creating thin pool with ${pool_size}G (80% of ${vg_free}G free)" sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} <&1 lvconvert --type thin-pool ${vg_name}/${pool_name} 2>&1 EOF if [ $? -eq 0 ]; then log_success "Thin pool $vg_name/$pool_name created" return 0 else log_error "Failed to create thin pool" return 1 fi } # Recreate storage in Proxmox recreate_storage() { local host=$1 local storage=$2 local vg_name=$3 local pool_name=$4 local node_name=$5 log_info "Recreating storage $storage on $host..." # Create thin pool if needed if ! check_thinpool_exists "$host" "$vg_name" "$pool_name"; then if ! create_thin_pool "$host" "$vg_name" "$pool_name"; then log_error "Cannot recreate storage without thin pool" return 1 fi fi # Add storage to Proxmox # Note: thinpool parameter should be just the pool name, not vg/pool sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} <&1 EOF if [ $? -eq 0 ]; then log_success "Storage $storage recreated successfully" return 0 else log_warn "Storage add command returned non-zero, checking if it exists..." # Check if it was created if sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} \ "pvesm status 2>/dev/null | grep -q '$storage'" 2>/dev/null; then log_success "Storage $storage exists (may have already been configured)" return 0 else log_error "Failed to recreate storage" return 1 fi fi } # Process a single node process_node() { local host=$1 local node_name=$2 log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log_info "Processing node: $node_name ($host)" log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" # Get current VG status log_info "Current volume groups:" sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} "vgs 2>/dev/null" || true echo "" # Process each storage pool for storage in "${STORAGE_POOLS[@]}"; do log_info "Processing storage: $storage" # Get VG and pool names from config (before removal) local vg_name=$(get_vg_for_storage "$host" "$storage") local pool_name=$(get_thinpool_for_storage "$host" "$storage") if [ -z "$vg_name" ]; then # Use storage name as VG name (common pattern) vg_name="$storage" pool_name="$storage" log_info "Using default: VG=$vg_name, Pool=$pool_name" else log_info "Found config: VG=$vg_name, Pool=$pool_name" fi # Check if storage is in use if ! check_storage_usage "$host" "$storage"; then log_warn "Storage $storage may be in use. Proceeding with caution..." fi # Remove storage remove_storage "$host" "$storage" # Recreate storage if recreate_storage "$host" "$storage" "$vg_name" "$pool_name" "$node_name"; then log_success "Storage $storage repaired on $node_name" else log_error "Failed to repair storage $storage on $node_name" fi echo "" done # Show final status log_info "Final storage status:" sshpass -p "$PVE_PASS" ssh -o StrictHostKeyChecking=accept-new root@${host} \ "pvesm status | grep -E '(thin1|thin2|thin3)'" || true echo "" } # Main execution main() { echo "" log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log_info "Repair and Redeploy Thin Storage Pools" log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" log_warn "This script will:" log_warn " 1. Remove thin1, thin2, thin3 storage configurations" log_warn " 2. Recreate them properly" log_warn " 3. Ensure they are properly configured" echo "" # Check for non-interactive mode if [[ "${NON_INTERACTIVE:-}" == "1" ]] || [[ ! -t 0 ]]; then log_info "Non-interactive mode: proceeding automatically" else read -p "Continue? (y/N): " -n 1 -r echo "" if [[ ! $REPLY =~ ^[Yy]$ ]]; then log_info "Operation cancelled" exit 0 fi fi # Process pve if sshpass -p "$PVE_PASS" ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new root@${PROXMOX_HOST_PVE} "echo 'connected'" 2>/dev/null; then process_node "$PROXMOX_HOST_PVE" "pve" else log_error "Cannot connect to pve" fi # Process pve2 if sshpass -p "$PVE_PASS" ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new root@${PROXMOX_HOST_PVE2} "echo 'connected'" 2>/dev/null; then process_node "$PROXMOX_HOST_PVE2" "pve2" else log_warn "Cannot connect to pve2, skipping..." fi echo "" log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log_info "Repair Complete" log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" log_info "Storage pools have been repaired and redeployed." log_info "Verify status with: ssh root@ 'pvesm status'" echo "" } main "$@"