#!/bin/bash # ============================================================ # EzScale Hypervisor Discovery Script # ============================================================ # Run from your JumpHost. Connects to each node via SSH as root # and collects hardware, storage, network, and VM info. # # Usage: # chmod +x ezscale-discover.sh # ./ezscale-discover.sh # # Output: Creates a report file per node + combined summary # ============================================================ set -euo pipefail NODES=("vf-node-01" "vf-node-02" "vf-node-03") SSH_USER="root" SSH_OPTS="-o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new" OUTPUT_DIR="./ezscale-discovery-$(date +%Y%m%d-%H%M%S)" SUMMARY_FILE="$OUTPUT_DIR/00-SUMMARY.txt" mkdir -p "$OUTPUT_DIR" # Colors for terminal output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' NC='\033[0m' echo -e "${CYAN}=========================================${NC}" echo -e "${CYAN} EzScale Hypervisor Discovery${NC}" echo -e "${CYAN}=========================================${NC}" echo "" # Remote command block — runs on each node read -r -d '' REMOTE_SCRIPT << 'REMOTECMD' || true #!/bin/bash echo "===== SYSTEM INFO =====" echo "--- Hostname ---" hostname -f 2>/dev/null || hostname echo "" echo "--- OS ---" cat /etc/os-release 2>/dev/null | grep -E "^(NAME|VERSION|PRETTY)" || echo "Unknown OS" echo "" echo "--- Kernel ---" uname -r echo "" echo "--- Uptime ---" uptime echo "" echo "===== CPU =====" echo "--- Model ---" grep "model name" /proc/cpuinfo | head -1 | cut -d: -f2 | xargs echo "" echo "--- Physical CPUs ---" grep "physical id" /proc/cpuinfo | sort -u | wc -l echo "" echo "--- Cores per CPU ---" grep "cpu cores" /proc/cpuinfo | head -1 | cut -d: -f2 | xargs echo "" echo "--- Total Threads ---" nproc echo "" echo "--- CPU Flags (virt) ---" grep -oE '(vmx|svm|ept|npt)' /proc/cpuinfo | sort -u | tr '\n' ' ' echo "" echo "" echo "--- lscpu summary ---" lscpu | grep -E "(Model name|Socket|Core|Thread|CPU\(s\)|MHz|cache)" 2>/dev/null echo "" echo "===== MEMORY =====" echo "--- Total / Used / Free ---" free -h echo "" echo "--- DIMM Details ---" dmidecode -t memory 2>/dev/null | grep -E "(Size|Speed|Type|Locator)" | grep -v "No Module" | head -40 || echo "dmidecode not available or no permission" echo "" echo "===== STORAGE - BLOCK DEVICES =====" echo "--- lsblk ---" lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,ROTA,MODEL 2>/dev/null || lsblk echo "" echo "--- Disk Models ---" lsblk -d -o NAME,SIZE,MODEL,ROTA,TRAN 2>/dev/null || echo "N/A" echo "" echo "===== STORAGE - FILESYSTEM =====" echo "--- df -h ---" df -h | grep -v tmpfs | grep -v udev echo "" echo "===== STORAGE - ZFS =====" if command -v zpool &>/dev/null; then echo "--- ZFS Pools ---" zpool list -v 2>/dev/null || echo "No ZFS pools" echo "" echo "--- ZFS Pool Status ---" zpool status 2>/dev/null || echo "N/A" echo "" echo "--- ZFS Datasets ---" zfs list -o name,used,avail,refer,mountpoint 2>/dev/null || echo "N/A" echo "" echo "--- ZFS Properties (compression, ashift, recordsize) ---" zfs get compression,ashift,recordsize -t filesystem 2>/dev/null | head -30 || echo "N/A" echo "" else echo "ZFS not installed" echo "" fi echo "===== STORAGE - LVM =====" if command -v pvs &>/dev/null; then echo "--- Physical Volumes ---" pvs 2>/dev/null || echo "No LVM PVs" echo "" echo "--- Volume Groups ---" vgs 2>/dev/null || echo "No LVM VGs" echo "" echo "--- Logical Volumes ---" lvs -o lv_name,vg_name,lv_size,lv_attr 2>/dev/null || echo "No LVM LVs" echo "" else echo "LVM not installed" echo "" fi echo "===== STORAGE - MDADM (Software RAID) =====" if [ -f /proc/mdstat ]; then cat /proc/mdstat else echo "No mdadm RAID" fi echo "" echo "===== NETWORK =====" echo "--- Interfaces ---" ip -br addr show 2>/dev/null || ifconfig -a 2>/dev/null echo "" echo "--- Interface Speeds ---" for iface in $(ls /sys/class/net/ | grep -v lo); do speed=$(cat /sys/class/net/$iface/speed 2>/dev/null || echo "N/A") driver=$(ethtool -i $iface 2>/dev/null | grep driver | awk '{print $2}' || echo "N/A") echo " $iface: ${speed}Mbps (driver: $driver)" done echo "" echo "--- Default Route ---" ip route | grep default echo "" echo "--- Bridge / Bond Config ---" if command -v brctl &>/dev/null; then brctl show 2>/dev/null || echo "No bridges" fi ip link show type bridge 2>/dev/null | grep -E "^[0-9]" || echo "" ip link show type bond 2>/dev/null | grep -E "^[0-9]" || echo "" echo "" echo "===== LIBVIRT / KVM =====" if command -v virsh &>/dev/null; then echo "--- Libvirt Version ---" virsh version --daemon 2>/dev/null || virsh version 2>/dev/null echo "" echo "--- All VMs (running + stopped) ---" virsh list --all echo "" echo "--- Running VM Count ---" echo "$(virsh list --state-running --name | grep -c .)" echo "" echo "--- VM Resource Usage ---" echo "VM_NAME | vCPUs | RAM_MAX | STATE" echo "--------|-------|---------|------" for vm in $(virsh list --name --state-running 2>/dev/null); do vcpus=$(virsh dominfo "$vm" 2>/dev/null | grep "CPU(s)" | awk '{print $2}') maxmem=$(virsh dominfo "$vm" 2>/dev/null | grep "Max memory" | awk '{print $3, $4}') state=$(virsh dominfo "$vm" 2>/dev/null | grep "State" | cut -d: -f2 | xargs) echo "$vm | $vcpus | $maxmem | $state" done echo "" echo "--- Total Allocated vCPUs (running VMs) ---" total_vcpus=0 for vm in $(virsh list --name --state-running 2>/dev/null); do v=$(virsh dominfo "$vm" 2>/dev/null | grep "CPU(s)" | awk '{print $2}') total_vcpus=$((total_vcpus + v)) done echo "$total_vcpus" echo "" echo "--- Total Allocated RAM (running VMs) ---" total_ram=0 for vm in $(virsh list --name --state-running 2>/dev/null); do r=$(virsh dominfo "$vm" 2>/dev/null | grep "Max memory" | awk '{print $3}') total_ram=$((total_ram + r)) done echo "$((total_ram / 1024)) MB ($((total_ram / 1024 / 1024)) GB)" echo "" echo "--- VM Disk Locations ---" for vm in $(virsh list --name --all 2>/dev/null); do echo "[$vm]" virsh domblklist "$vm" --details 2>/dev/null | grep -E "file.*disk" || echo " (no disks found)" done echo "" echo "--- Storage Pools ---" virsh pool-list --all 2>/dev/null || echo "No storage pools" echo "" for pool in $(virsh pool-list --name 2>/dev/null); do echo "--- Pool: $pool ---" virsh pool-info "$pool" 2>/dev/null echo "" done else echo "libvirt/virsh not installed" echo "" fi echo "===== QEMU =====" if command -v qemu-system-x86_64 &>/dev/null; then echo "--- QEMU Version ---" qemu-system-x86_64 --version 2>/dev/null | head -1 elif command -v kvm &>/dev/null; then echo "--- KVM Version ---" kvm --version 2>/dev/null | head -1 else echo "QEMU binary not found in PATH" fi echo "" echo "===== DISK USAGE BY VM IMAGES =====" echo "--- qcow2 files ---" find / -name "*.qcow2" -type f 2>/dev/null | while read f; do size=$(du -h "$f" 2>/dev/null | awk '{print $1}') virtual=$(qemu-img info "$f" 2>/dev/null | grep "virtual size" | awk '{print $3, $4}' || echo "N/A") echo " $f (actual: $size, virtual: $virtual)" done echo "" echo "--- raw disk files ---" find / -name "*.raw" -type f 2>/dev/null | while read f; do size=$(du -h "$f" 2>/dev/null | awk '{print $1}') echo " $f (actual: $size)" done echo "" echo "===== SERVICES =====" echo "--- Key Services Status ---" for svc in libvirtd qemu-guest-agent virtfusion virtfusion-agent zfs-zed zfs-import-cache zfs-mount; do status=$(systemctl is-active "$svc" 2>/dev/null || echo "not-found") echo " $svc: $status" done echo "" echo "===== RESOURCE SUMMARY =====" echo "--- CPU ---" total_threads=$(nproc) allocated_vcpus=$total_vcpus echo " Total threads: $total_threads" echo " Allocated vCPUs: $allocated_vcpus" echo " Overcommit ratio: $(echo "scale=2; $allocated_vcpus / $total_threads" | bc 2>/dev/null || echo 'N/A')" echo "" echo "--- RAM ---" total_ram_mb=$(free -m | awk '/Mem:/ {print $2}') echo " Total RAM: ${total_ram_mb} MB ($((total_ram_mb / 1024)) GB)" echo " Allocated to VMs: $((total_ram / 1024)) MB ($((total_ram / 1024 / 1024)) GB)" echo " Free for host/new VMs: $((total_ram_mb - total_ram / 1024)) MB" echo " Utilization: $(echo "scale=1; $total_ram / 1024 / $total_ram_mb * 100" | bc 2>/dev/null || echo 'N/A')%" echo "" echo "===== END =====" REMOTECMD # Run on each node for node in "${NODES[@]}"; do report_file="$OUTPUT_DIR/${node}.txt" echo -e "${YELLOW}Connecting to ${node}...${NC}" if ssh $SSH_OPTS ${SSH_USER}@${node} "echo ok" &>/dev/null; then echo -e "${GREEN} Connected. Gathering data...${NC}" ssh $SSH_OPTS ${SSH_USER}@${node} "$REMOTE_SCRIPT" > "$report_file" 2>&1 echo -e "${GREEN} Done → ${report_file}${NC}" else echo -e "${RED} FAILED to connect to ${node}${NC}" echo "CONNECTION FAILED" > "$report_file" fi echo "" done # Build combined summary echo -e "${CYAN}Building summary...${NC}" { echo "============================================" echo " EzScale Infrastructure Discovery Summary" echo " Generated: $(date)" echo "============================================" echo "" for node in "${NODES[@]}"; do report_file="$OUTPUT_DIR/${node}.txt" if [ -f "$report_file" ] && ! grep -q "CONNECTION FAILED" "$report_file"; then echo "============================================" echo " NODE: ${node}" echo "============================================" # Extract key metrics — use "-- PATTERN" so grep doesn't # interpret leading dashes as options echo "" echo " OS: $(grep -A1 -- '--- OS ---' "$report_file" | grep 'PRETTY' | cut -d= -f2 | tr -d '"')" echo " Kernel: $(grep -A1 -- '--- Kernel ---' "$report_file" | tail -1)" echo " CPU: $(grep -A1 -- '--- Model ---' "$report_file" | tail -1)" echo " Sockets: $(grep -A1 -- '--- Physical CPUs ---' "$report_file" | tail -1)" echo " Cores: $(grep -A1 -- '--- Cores per CPU ---' "$report_file" | tail -1)" echo " Threads: $(grep -A1 -- '--- Total Threads ---' "$report_file" | tail -1)" # RAM total_ram=$(grep -A2 -- '--- Total / Used / Free ---' "$report_file" | grep 'Mem:' | awk '{print $2}') used_ram=$(grep -A2 -- '--- Total / Used / Free ---' "$report_file" | grep 'Mem:' | awk '{print $3}') echo " RAM: ${total_ram} total, ${used_ram} used" # VM counts running=$(grep -A1 -- '--- Running VM Count ---' "$report_file" | tail -1) echo " VMs: ${running} running" # Allocated resources alloc_vcpus=$(grep -A1 -- '--- Total Allocated vCPUs' "$report_file" | tail -1) alloc_ram=$(grep -A1 -- '--- Total Allocated RAM' "$report_file" | tail -1) echo " Alloc: ${alloc_vcpus} vCPUs, ${alloc_ram} RAM" # Overcommit overcommit=$(grep -A5 -- '--- CPU ---' "$report_file" | grep 'Overcommit' | awk '{print $NF}') ram_util=$(grep -A8 -- '--- RAM ---' "$report_file" | grep 'Utilization' | awk '{print $NF}') echo " CPU overcommit: ${overcommit}x" echo " RAM utilization: ${ram_util}" echo "" else echo "============================================" echo " NODE: ${node} — CONNECTION FAILED" echo "============================================" echo "" fi done } > "$SUMMARY_FILE" echo -e "${GREEN}Summary → ${SUMMARY_FILE}${NC}" echo "" echo -e "${CYAN}=========================================${NC}" echo -e "${CYAN} Discovery Complete${NC}" echo -e "${CYAN}=========================================${NC}" echo "" echo "Full reports: ${OUTPUT_DIR}/" echo "Summary: ${SUMMARY_FILE}" echo "" echo "Next step: Share the contents of ${OUTPUT_DIR}/ and I can" echo "analyze your capacity and finalize the product line."