All pages now use native Vuetify components directly. Flash messages are handled by the ToastStack component via Pinia store. Notifications use NotificationPanel. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
355 lines
12 KiB
Bash
Executable File
355 lines
12 KiB
Bash
Executable File
#!/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."
|