#!/bin/bash
###############################################################################
# check.sh
#
# READ-ONLY, comprehensive Proxmox host assessment.
#   - Installs NOTHING. Modifies NOTHING. Writes only its own log.
#   - Detects what is/is isn't installed so the next step can be informed.
#   - Runtime ~30-60 seconds on a busy node.
#
# Output:  /root/<short-hostname>.log
#
# Usage (as root):
#     bash check.sh
###############################################################################
set -u
umask 077

HOST="$(hostname -s)"
OUT="/root/${HOST}.log"
: > "$OUT"
TS="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
START_EPOCH="$(date +%s)"

# ===========================================================================
# Helpers (pattern from project memory)
# ===========================================================================
blank(){ printf '\n' >> "$OUT"; }

hdr() {
    {
        printf '\n'
        printf '###############################################################################\n'
        printf '# %s\n' "$1"
        printf '###############################################################################\n'
    } >> "$OUT"
}

# cmd LABEL CMD [ARGS...] - simple commands only (no pipes/loops/&&)
cmd() {
    local label="$1"; shift
    {
        printf '\n--- %s\n' "$label"
        printf '    $ %s\n' "$*"
    } >> "$OUT"
    "$@" >> "$OUT" 2>&1 || printf '    [exit=%s / not installed or not permitted]\n' "$?" >> "$OUT"
}

# cmdsh LABEL <<'EOF' ... EOF - arbitrary shell snippet (pipes, loops OK).
# Heredoc body MUST use quoted 'EOF' so $vars pass through to eval literally;
# export any outer-scope vars the body needs before the call.
# shellcheck disable=SC2310
cmdsh() {
    local label="$1" snippet
    snippet=$(cat)
    {
        printf '\n--- %s\n' "$label"
        printf '    $ %s\n' "$snippet"
    } >> "$OUT"
    eval "$snippet" >> "$OUT" 2>&1 || printf '    [exit=%s]\n' "$?" >> "$OUT"
}

# filecat LABEL PATH [PATH...] - dump file contents (cap 300 lines each)
filecat() {
    local label="$1"; shift
    printf '\n--- %s\n' "$label" >> "$OUT"
    local f
    for f in "$@"; do
        if [ -r "$f" ]; then
            printf '    --- %s ---\n' "$f" >> "$OUT"
            sed -n '1,300p' "$f" >> "$OUT"
        else
            printf '    [missing: %s]\n' "$f" >> "$OUT"
        fi
    done
}

# dirlist LABEL PATH - list directory contents (existence + sizes)
dirlist() {
    local label="$1" d="$2"
    printf '\n--- %s\n' "$label" >> "$OUT"
    if [ -d "$d" ]; then
        ls -la "$d" >> "$OUT" 2>&1
    else
        printf '    [missing: %s]\n' "$d" >> "$OUT"
    fi
}

# ===========================================================================
# Header
# ===========================================================================
{
    printf '###############################################################################\n'
    printf '# Proxmox VE host performance assessment  (READ-ONLY)\n'
    printf '# Host:      %s (%s)\n' "$HOST" "$(hostname -f 2>/dev/null)"
    printf '# FQDN:      %s\n' "$(hostname -f 2>/dev/null)"
    printf '# All IPs:   %s\n' "$(hostname -I 2>/dev/null)"
    printf '# Collected: %s (UTC)\n' "$TS"
    printf '# Kernel:    %s\n' "$(uname -r)"
    printf '# Machine:   %s %s\n' "$(uname -m)" "$(uname -o)"
    printf '# Caller:    %s (uid=%s)\n' "$(whoami)" "$(id -u)"
    printf '# Script:    check.sh  -  strictly READ-ONLY\n'
    printf '###############################################################################\n'
} >> "$OUT"

# ===========================================================================
# 1. TOOL AVAILABILITY MATRIX
#    (most important section: tells us what is/isn't installed)
# ===========================================================================
hdr "1. TOOL AVAILABILITY MATRIX"

cmdsh "command presence + path + version" <<'EOF'
# Each line: name | present? | path | version
printf '%-22s %-8s %-30s %s\n' "TOOL" "PRESENT" "PATH" "VERSION/NOTE"
printf '%-22s %-8s %-30s %s\n' "----" "-------" "----" "------------"
for t in \
    pveversion pvecm qm pct pvesm pvesh pveperf proxmox-backup-client \
    ip ss ethtool bridge brctl nmcli \
    tcpdump mtr nstat netstat nicstat ifstat \
    dmidecode lscpu lshw hwinfo inxi lspci lsusb numactl numastat \
    cpupower turbostat x86_energy_perf_policy rdmsr \
    tuned-adm irqbalance \
    zpool zfs zdb arcstat arc_summary \
    lsblk blkid blockdev findmnt smartctl nvme hdparm \
    storcli storcli64 MegaCli MegaCli64 tw-cli arcconf \
    sas2ircu sas3ircu hpssacli ssacli perccli \
    multipath pvs vgs lvs lvdisplay \
    xfs_info dumpe2fs btrfs \
    fio iperf iperf3 sysbench bonnie++ iozone \
    iostat mpstat pidstat sar vmstat \
    top htop atop glances dstat nethogs iftop iotop smem pmap slabtop \
    perf strace lsof fatrace \
    dig host nslookup \
    chronyc ntpq timedatectl \
    nft iptables iptables-save ipset ufw \
    jq bc awk smartctl nvme \
    apt dpkg
do
    p="$(command -v "$t" 2>/dev/null)"
    if [ -n "$p" ]; then
        v=""
        case "$t" in
            zpool|zfs)        v="$(timeout 3 "$t" version 2>/dev/null | head -n 2 | tr '\n' ' ')" ;;
            iperf3|fio|sysbench|smartctl|nvme|tuned-adm|ethtool|cpupower)
                v="$(timeout 3 "$t" --version 2>&1 | head -n 1)" ;;
            pveversion)       v="$(timeout 3 "$t" 2>/dev/null)" ;;
            storcli64|storcli|MegaCli64|MegaCli|arcconf|sas2ircu|sas3ircu|ssacli|hpssacli)
                v="(storage HBA CLI - present)" ;;
        esac
        printf '%-22s %-8s %-30s %s\n' "$t" "yes" "$p" "$v"
    else
        printf '%-22s %-8s %-30s %s\n' "$t" "no" "-" "-"
    fi
done
EOF

cmdsh "Debian packages relevant to perf/storage/net" <<'EOF'
dpkg -l 2>/dev/null | awk '/^ii/ && ($2 ~ /^(proxmox-|pve-|zfs|zfsutils|nfs|nfs-kernel-server|nfs-common|open-iscsi|ceph|tuned|irqbalance|sysstat|smartmontools|nvme-cli|ethtool|bridge-utils|ifenslave|fio|iperf3|sysbench|glances|htop|atop|lm-sensors|numactl|cpufrequtils|linux-image-|pve-kernel|qemu|libvirt|corosync|pacemaker)/) {print $2"  "$3}' | head -n 80
EOF

cmdsh "kernel modules available (relevant to perf)" <<'EOF'
for m in kvm_intel kvm_amd \
         ixgbe i40e i40iw ice igb igc mlx4_en mlx5_core mlx4_core bnx2x bnx2 tg3 be2net qed qede virtio_net \
         nvme nvme-tcp nvme-fabrics sd_mod sr_mod \
         zfs spl znvpair zcommon zunicode zavl icp zlua \
         megaraid_sas mpt3sas hpsa aacraid smartpqi isci \
         dm_multipath multipath \
         tcp_dctcp tcp_bbr tcp_cubic tcp_htcp \
         8021q bonding bridge br_netfilter \
         vhost_net vhost_scsi target_core_mod \
         nfsv4 nfsv3 rpcsec_gss_krb5
do
    modinfo "$m" >/dev/null 2>&1 && printf 'available : %s (%s)\n' "$m" "$(modinfo -F version "$m" 2>/dev/null || echo built-in)"
done
EOF

cmdsh "currently loaded modules (filtered)" <<'EOF'
lsmod | awk 'NR==1 || $1 ~ /^(kvm|virtio|vhost|ixgb|i40e|ice|igb|mlx|bnx|tg3|be2|qed|nvme|zfs|spl|z|dm_|megaraid|mpt|hpsa|aacraid|smartpqi|8021q|bonding|bridge|nfsv|tcp_|rpc|sunrpc|loop)/{print}'
EOF

# ===========================================================================
# 2. OS / DISTRO / UPTIME
# ===========================================================================
hdr "2. OS / DISTRO / UPTIME"
filecat "os-release"             /etc/os-release
cmd "debian version"             cat /etc/debian_version
cmd "uptime"                     uptime
cmd "uptime raw (since boot)"    cat /proc/uptime
cmd "last reboot"                last -x reboot 2>/dev/null | head -n 5
cmd "last logins"                last 2>/dev/null | head -n 10
cmd "uname"                      uname -a
cmd "/proc/cmdline (boot args)"  cat /proc/cmdline

# ===========================================================================
# 3. HARDWARE INVENTORY
# ===========================================================================
hdr "3. HARDWARE INVENTORY"
cmd "dmidecode -t system"        dmidecode -t system
cmd "dmidecode -t baseboard"     dmidecode -t baseboard
cmd "dmidecode -t bios"          dmidecode -t bios
cmd "dmidecode -t chassis"       dmidecode -t chassis
cmd "dmidecode -t processor"     dmidecode -t processor
cmd "dmidecode -t memory"        dmidecode -t memory
cmd "dmidecode -t cache"         dmidecode -t cache
cmd "dmidecode -t connector"     dmidecode -t connector
cmd "dmidecode -t slot"          dmidecode -t slot
cmd "lscpu"                      lscpu
cmd "lscpu cache"                lscpu -C
cmd "numactl --hardware"         numactl --hardware
cmd "numactl --show"             numactl --show
cmd "lspci -nnvv (verbose)"      lspci -nnvv
cmd "lspci -tv (tree)"           lspci -tv
cmd "lspci numa nodes"           cat /sys/bus/pci/devices/*/numa_node 2>/dev/null | sort | uniq -c
cmd "lsusb"                      lsusb
cmd "lshw -short"                lshw -short 2>/dev/null
dirlist "PCIe AER errors"        /sys/bus/pci/devices/*/aer_dev_correctable
cmdsh "PCIe AER summary (errors matter for NIC/NVMe stability)" <<'EOF'
found=0
for d in /sys/bus/pci/devices/*; do
    [ -d "$d" ] || continue
    addr="$(basename "$d")"
    class="$(cat "$d/class" 2>/dev/null)"
    case "$class" in
        0x020000|0x010802|0x010700) ;;  # ethernet, nvme, raid
        *) continue ;;
    esac
    for f in "$d"/aer_dev_*; do
        [ -r "$f" ] || continue
        val="$(cat "$f" 2>/dev/null)"
        [ "$val" = "0" ] && continue
        [ "$val" = "" ] && continue
        printf '%s %s = %s\n' "$addr" "$(basename "$f")" "$val"
        found=1
    done
done
[ "$found" = "0" ] && echo "  no AER errors on NIC/NVMe/RAID devices"
EOF

# ===========================================================================
# 4. CPU POWER / IDLE / MICROCODE
# ===========================================================================
hdr "4. CPU POWER / FREQUENCY / IDLE STATES"

cmdsh "scaling_governor per cpu (counts)" <<'EOF'
for c in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
    [ -e "$c" ] || continue
    cat "$c"
done | sort | uniq -c
EOF

cmdsh "scaling_driver / available governors / freq limits" <<'EOF'
for f in /sys/devices/system/cpu/cpu0/cpufreq/*; do
    [ -r "$f" ] || continue
    [ -d "$f" ] && continue
    printf '%-40s = %s\n' "$(basename "$f")" "$(cat "$f" 2>/dev/null)"
done
EOF

cmdsh "EPP / EPB / intel_pstate" <<'EOF'
echo "intel_pstate status: $(cat /sys/devices/system/cpu/intel_pstate/status 2>/dev/null)"
echo "intel_pstate no_turbo: $(cat /sys/devices/system/cpu/intel_pstate/no_turbo 2>/dev/null)"
echo "intel_pstate turbo_pct: $(cat /sys/devices/system/cpu/intel_pstate/turbo_pct 2>/dev/null)"
echo "intel_pstate max_perf_pct: $(cat /sys/devices/system/cpu/intel_pstate/max_perf_pct 2>/dev/null)"
echo "intel_pstate min_perf_pct: $(cat /sys/devices/system/cpu/intel_pstate/min_perf_pct 2>/dev/null)"
echo "EPB (energy_perf_bias) per cpu:"
for c in /sys/devices/system/cpu/cpu*/power/energy_perf_bias; do
    [ -r "$c" ] || continue
    printf '  %s : %s\n' "$(echo "$c" | cut -d/ -f5)" "$(cat "$c" 2>/dev/null)"
done | sort | uniq -c
echo "EPP (energy_performance_preference) per cpu:"
for c in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do
    [ -r "$c" ] || continue
    printf '  %s : %s\n' "$(echo "$c" | cut -d/ -f5)" "$(cat "$c" 2>/dev/null)"
done | sort | uniq -c
echo "available EPP:"
cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_available_preferences 2>/dev/null
EOF

cmd "cpupower frequency-info"    cpupower frequency-info 2>/dev/null
cmd "turbostat snapshot"         timeout 3 turbostat --quiet --show CPU,Core,Bzy_MHz,IRQ,Busy%,Bzy_MHz,TSC_MHz,CoreTmp,PkgTmp sleep 1 2>/dev/null
cmd "x86_energy_perf_policy -r"  x86_energy_perf_policy -r 2>/dev/null

cmdsh "C-states (idle) enabled per cpu0" <<'EOF'
for s in /sys/devices/system/cpu/cpu0/cpuidle/state*; do
    [ -d "$s" ] || continue
    printf '  %s : %s disabled=%s  latency=%sus  residency=%sus\n' \
        "$(cat "$s/name" 2>/dev/null)" \
        "$(cat "$s/desc" 2>/dev/null)" \
        "$(cat "$s/disable" 2>/dev/null)" \
        "$(cat "$s/latency" 2>/dev/null)" \
        "$(cat "$s/time" 2>/dev/null)"
done
EOF

cmd "THP enabled"                cat /sys/kernel/mm/transparent_hugepage/enabled
cmd "THP defrag"                 cat /sys/kernel/mm/transparent_hugepage/defrag
cmd "THP khugepaged pages_to_scan" cat /sys/kernel/mm/transparent_hugepage/khugepaged/pages_to_scan
cmd "THP khugepaged defrag"      cat /sys/kernel/mm/transparent_hugepage/khugepaged/defrag
cmd "microcode"                  dmesg 2>/dev/null | grep -i microcode | tail -n 5
cmd "cpu MHz (live snapshot)"    grep -c '^processor' /proc/cpuinfo; head -n 30 /proc/cpuinfo

# ===========================================================================
# 5. MEMORY / NUMA / SWAP
# ===========================================================================
hdr "5. MEMORY / NUMA / SWAP"
cmd "free -h"                    free -h
cmd "free -b"                    free -b
cmd "swapon"                     swapon --show
cmd "/proc/meminfo highlights"   grep -E 'MemTotal|MemFree|MemAvailable|Buffers|^Cached|SwapTotal|SwapFree|Hugepagesize|HugePages_Total|HugePages_Free|AnonHugePages|DirectMap|Slab|SReclaimable|SUnreclaim|Dirty|Writeback|AnonPages|Mapped|Shmem|KernelStack|PageTables|NFS_Unstable' /proc/meminfo
cmd "numa stat"                  cat /proc/sys/vm/numa_stat 2>/dev/null
cmd "numa_balancing"             cat /proc/sys/kernel/numa_balancing 2>/dev/null
cmd "zone reclaim"               cat /proc/sys/vm/zone_reclaim_mode 2>/dev/null
cmd "slab top (top 20)"          slabtop -o -s c 2>/dev/null | head -n 25
cmd "kswapd stats"               grep -E 'kswapd|pgmigrate|pgmigrate_failed|compact_' /proc/vmstat 2>/dev/null | head -n 30

# ===========================================================================
# 6. KERNEL TUNABLES
# ===========================================================================
hdr "6. KERNEL TUNABLES / SYSCTL"

cmdsh "vm.* tunables" <<'EOF'
sysctl -a 2>/dev/null | grep -E '^vm\.(dirty_(ratio|background_ratio|bytes|background_bytes|expire_centisecs|writeback_centisecs)|swappiness|vfs_cache_pressure|min_free_kbytes|watermark_scale_factor|overcommit_memory|overcommit_ratio|nr_hugepages|nr_overcommit_hugepages|zone_reclaim_mode|compact_unevictable_allowed|stat_interval|admin_reserve_kbytes|user_reserve_kbytes)'
EOF

cmdsh "fs.* tunables" <<'EOF'
sysctl -a 2>/dev/null | grep -E '^fs\.(file-max|file-nr|aio-max-nr|aio-nr|nr_open|pipe-max-size|epoll/max_user_watches|lease-break-time|mqueue|max_user_ns)'
EOF

cmdsh "kernel.* perf-relevant" <<'EOF'
sysctl -a 2>/dev/null | grep -E '^kernel\.(sched_(child_runs_first|migration_cost|domain|rt_period|rt_runtime|idle)|numa_balancing|watchdog|nmi_watchdog|hung_task|randomize_va_space|max_pid|threads-max|sched_time_avg)'
EOF

# ===========================================================================
# 7. NETWORK SYSCTL / TCP
# ===========================================================================
hdr "7. NETWORK SYSCTL / TCP"
cmdsh "net.core.* tunables" <<'EOF'
sysctl -a 2>/dev/null | grep -E '^net\.core\.(rmem_default|rmem_max|wmem_default|wmem_max|optmem_max|somaxconn|netdev_max_backlog|netdev_budget|netdev_budget_usecs|netdev_tstamp_prequeue|rps_flow_cnt|busy_poll|busy_read|default_qdisc|dev_weight|xfrm)'
EOF

cmdsh "net.ipv4.tcp.* tunables" <<'EOF'
sysctl -a 2>/dev/null | grep -E '^net\.ipv4\.tcp_(rmem|wmem|congestion_control|available_congestion_control|mtu_probing|slow_start_after_idle|no_metrics_save|timestamps|window_scaling|sack|low_latency|fastopen|ecn|ecn_fallback|syncookies|max_syn_backlog|tw_reuse|fin_timeout|keepalive_time|keepalive_probes|keepalive_intvl|thp_linear_timeouts|pacing|pacing_ss_ratio|pacing_ca_ratio|no_ssthresh_metrics_save|md5sig|abort_on_overflow|buffer_split)'
EOF

cmdsh "other net.* sysctls" <<'EOF'
sysctl -a 2>/dev/null | grep -E '^net\.(ipv4\.ip_(local_port_range|local_reserved_ports|no_pmtu_disc|forwarding)|ipv4\.conf\.all\.(rp_filter|accept_redirects|send_redirects)|ipv4\.conf\.default\.forwarding|core\.netdev_budget|ipv6\.conf\.all\.disable_ipv6)'
EOF

dirlist "sysctl.d"               /etc/sysctl.d
filecat "/etc/sysctl.conf"       /etc/sysctl.conf
cmdsh "non-default sysctl values (sorted, filtered)" <<'EOF'
# Show only sysctls that differ from running state vs an empty config;
# too noisy to dump all of /proc/sys. Just show /etc/sysctl.d content.
for f in /etc/sysctl.d/*.conf /etc/sysctl.conf; do
    [ -r "$f" ] || continue
    echo "=== $f ==="
    grep -vE '^\s*#|^\s*$' "$f" 2>/dev/null
done
EOF

# ===========================================================================
# 8. tuned / irqbalance / udev
# ===========================================================================
hdr "8. tuned / irqbalance / udev"
cmd "tuned-adm active"           tuned-adm active 2>/dev/null
cmd "tuned-adm list"             tuned-adm list 2>/dev/null
cmd "systemctl is-active tuned"  systemctl is-active tuned 2>/dev/null
cmd "irqbalance status"          systemctl is-active irqbalance 2>/dev/null
filecat "irqbalance config"      /etc/default/irqbalance /etc/irqbalance.conf
dirlist "modprobe.d"             /etc/modprobe.d
dirlist "udev rules.d"           /etc/udev/rules.d
filecat "modprobe.d files"       /etc/modprobe.d/*.conf
cmdsh "module params: kvm_intel" <<'EOF'
for p in /sys/module/kvm_intel/parameters/*; do
    [ -e "$p" ] && printf '  %s = %s\n' "$(basename "$p")" "$(cat "$p" 2>/dev/null)"
done
EOF
cmdsh "module params: kvm_amd" <<'EOF'
for p in /sys/module/kvm_amd/parameters/*; do
    [ -e "$p" ] && printf '  %s = %s\n' "$(basename "$p")" "$(cat "$p" 2>/dev/null)"
done
EOF
cmdsh "module params: relevant net drivers" <<'EOF'
for mod in i40e ixgbe mlx5_core igb ice bnx2x virtio_net; do
    [ -d "/sys/module/$mod/parameters" ] || continue
    echo "=== $mod ==="
    for p in /sys/module/$mod/parameters/*; do
        [ -e "$p" ] && printf '  %s = %s\n' "$(basename "$p")" "$(cat "$p" 2>/dev/null)"
    done
done
EOF

# ===========================================================================
# 9. PROXMOX / CLUSTER
# ===========================================================================
hdr "9. PROXMOX VERSION & CLUSTER"
cmd "pveversion"                 pveversion
cmd "pveversion -v"              pveversion -v
cmd "pvecm status"               pvecm status
cmd "pvecm nodes"                pvecm nodes
cmd "pvecm expected"             pvecm expected 2>/dev/null
cmd "pveperf"                    timeout 15 pveperf 2>/dev/null
filecat "corosync.conf"          /etc/pve/corosync.conf
filecat "datacenter.cfg"         /etc/pve/datacenter.cfg
filecat "storage.cfg"            /etc/pve/storage.cfg
filecat "user.cfg"               /etc/pve/user.cfg 2>/dev/null
filecat "domains.cfg"            /etc/pve/domains.cfg 2>/dev/null
cmdsh "cluster resources (compact)" <<'EOF'
pvesh get /cluster/resources --type node --output-format json 2>/dev/null \
  | jq -r '.[] | "\(.node)\tcpu=\(.cpu)\tmem=\(.mem)\tmaxmem=\(.maxmem)\tmaxdisk=\(.maxdisk)\tstatus=\(.status)\tssl_fingerprint=\(.ssl_fingerprint)"' 2>/dev/null \
  || pvesh get /cluster/resources --type node 2>/dev/null
EOF

# ===========================================================================
# 10. NETWORK: INTERFACES
# ===========================================================================
hdr "10. NETWORK: INTERFACES"
cmd "ip -d link"                 ip -d link
cmd "ip -o link"                 ip -o link
cmd "ip -o -4 addr"              ip -o -4 addr
cmd "ip -o -6 addr (link-local filter)" ip -o -6 addr 2>/dev/null | grep -v 'scope link'
cmd "ip addr"                    ip addr
cmd "ip route"                   ip route
cmd "ip rule"                    ip rule
cmd "ip -s -s link (counters/errors)" ip -s -s link
cmd "ip neigh"                   ip neigh
cmd "/proc/net/dev (counters)"   cat /proc/net/dev
cmd "ip -6 route (brief)"        ip -6 route 2>/dev/null | head -n 20

# Build NETIFS list once, export for use in heredoc snippets
NETIFS=""
for _n in /sys/class/net/*; do
    _n="$(basename "$_n")"
    case "$_n" in
        lo|sit0|ip6tnl0|gre0|gretap0|erspan0|bonding_masters|tailscale0|wg0|tunl0) continue ;;
    esac
    NETIFS="${NETIFS:+$NETIFS }$_n"
done
export NETIFS

cmdsh "sysfs interface attributes" <<'EOF'
for ifc in $NETIFS; do
    d="/sys/class/net/$ifc"
    [ -d "$d" ] || continue
    printf '%-16s speed=%-8s duplex=%-8s mtu=%-6s carrier=%-3s tx_queue_len=%-5s qlen=%-5s type=%s\n' \
        "$ifc" \
        "$(cat "$d/speed" 2>/dev/null)" \
        "$(cat "$d/duplex" 2>/dev/null)" \
        "$(cat "$d/mtu" 2>/dev/null)" \
        "$(cat "$d/carrier" 2>/dev/null)" \
        "$(cat "$d/tx_queue_len" 2>/dev/null)" \
        "$(cat "$d/tx_queue_len" 2>/dev/null)" \
        "$(cat "$d/type" 2>/dev/null)"
done
EOF

# Per-interface deep ethtool dump
cmdsh "ethtool per-interface deep dump" <<'EOF'
for ifc in $NETIFS; do
    [ -e "/sys/class/net/$ifc" ] || continue
    echo ""
    echo "----- ethtool: $ifc -----"
    echo "[driver/version/firmware/bus-info -i]"
    ethtool -i "$ifc" 2>&1
    echo "[settings - speed/duplex/autoneg/port]"
    ethtool "$ifc" 2>&1
    echo "[channels -l (RX/TX/combined queue counts)]"
    ethtool -l "$ifc" 2>&1
    echo "[coalesce -c (interrupt moderation)]"
    ethtool -c "$ifc" 2>&1
    echo "[ring -g (descriptor counts)]"
    ethtool -g "$ifc" 2>&1
    echo "[pause / flow-control -a]"
    ethtool -a "$ifc" 2>&1
    echo "[offload features -k]"
    ethtool -k "$ifc" 2>&1
    echo "[private flags --show-priv-flags]"
    ethtool --show-priv-flags "$ifc" 2>&1
    echo "[ntuple/RSS -n]"
    ethtool -n "$ifc" 2>&1 | head -n 40
    echo "[SFP/DOM transceiver -m]"
    ethtool -m "$ifc" 2>&1
    echo "[statistics -S (key error/drop counters only)]"
    ethtool -S "$ifc" 2>/dev/null \
        | grep -iE 'drop|discard|error|crc|miss|pause|fifo|no_buff|rx_no_|_err|fw_|_bad|oversiz|undersize|frag|jabber|length_err|alignment' \
        | head -n 60
    echo "[statistics -S (throughput counters)]"
    ethtool -S "$ifc" 2>/dev/null \
        | grep -iE 'rx_(octets|bytes|packets)|tx_(octets|bytes|packets)|rx_(unicast|multicast|broadcast)|tx_(unicast|multicast|broadcast)' \
        | head -n 30
done
EOF

# ===========================================================================
# 11. NETWORK: BRIDGES / BONDS / VLANS
# ===========================================================================
hdr "11. NETWORK: BRIDGES / BONDS / VLANS"
cmd "bridge link"                bridge link 2>/dev/null
cmd "bridge -d link"             bridge -d link 2>/dev/null
cmd "bridge vlan"                bridge vlan 2>/dev/null
cmd "bridge vlan show"           bridge vlan show 2>/dev/null
cmd "brctl show"                 brctl show 2>/dev/null
filecat "bonding state"          /proc/net/bonding/bond*
cmd "vlan config"                cat /proc/net/vlan/config 2>/dev/null
dirlist "/proc/net/vlan"         /proc/net/vlan
filecat "/etc/network/interfaces" /etc/network/interfaces
cmdsh "interfaces.d (per file)" <<'EOF'
for f in /etc/network/interfaces.d/*; do
    [ -r "$f" ] || continue
    echo "--- $f ---"
    cat "$f"
done
EOF

# ===========================================================================
# 12. NETWORK: IRQ / RPS / XPS / aRFS / SOFTNET
# ===========================================================================
hdr "12. NETWORK: IRQ / RPS / XPS / aRFS / SOFTNET"
cmd "/proc/interrupts"           cat /proc/interrupts

cmdsh "NIC IRQ affinities (which CPUs handle which queue)" <<'EOF'
for ifc in $NETIFS; do
    [ -d "/sys/class/net/$ifc/device/msi_irqs" ] || continue
    echo "=== $ifc IRQ list (first 40) ==="
    ls "/sys/class/net/$ifc/device/msi_irqs" 2>/dev/null | head -n 40
done
EOF

cmdsh "RPS masks (non-zero only)" <<'EOF'
for q in /sys/class/net/*/queues/rx-*/rps_cpus; do
    [ -e "$q" ] || continue
    val=$(cat "$q")
    [ "$val" != "0" ] && printf '  %s = %s\n' "$q" "$val"
done | head -n 60
EOF

cmdsh "XPS masks (non-zero only)" <<'EOF'
for q in /sys/class/net/*/queues/tx-*/xps_cpus; do
    [ -e "$q" ] || continue
    val=$(cat "$q")
    [ "$val" != "0" ] && printf '  %s = %s\n' "$q" "$val"
done | head -n 60
EOF

cmdsh "RFS flow counts" <<'EOF'
for q in /sys/class/net/*/queues/rx-*/rps_flow_cnt; do
    [ -e "$q" ] || continue
    printf '  %s = %s\n' "$q" "$(cat "$q")"
done | head -n 60
EOF

cmd "/proc/net/softnet_stat (drops/backlog)"  cat /proc/net/softnet_stat
cmdsh "softnet_stat decoded" <<'EOF'
# columns: TOTAL DROPPED DROPPED_BY_NETRX TIME_SQUEEZED CPU_COLLISIONS RPS_RECEIVED_FLOW_LIMIT
awk '{ printf "cpu%-2d total=%-8s dropped=%-6s time_squeezed=%-6s cpu_collision=%-4s rps_received=%-6s flow_limit=%s\n",
       NR-1, $1, $2, $4, $5, $6, $7 }' /proc/net/softnet_stat
EOF

cmdsh "netstat drop counters (significant ones)" <<'EOF'
nstat -az 2>/dev/null | grep -iE '(Drop|Retrans|Fail|Abort|Overflow|Loss|ListenDrop|EmbryonicRsts|InErrs|OutErrs)' || \
netstat -s 2>/dev/null | grep -iE '(drop|retrans|fail|abort|overflow|loss|listen|error)' | head -n 60
EOF

# ===========================================================================
# 13. NETWORK: SOCKET / TCP STATE
# ===========================================================================
hdr "13. NETWORK: SOCKET / TCP STATE"
cmd "ss -s"                      ss -s
cmd "ss -t -m (top TCP with skmem/cwnd/rto)"  ss -tno state established '( dport != :22 and sport != :22 )' -m 2>/dev/null | head -n 40
cmd "ss -ti (TCP internals)"     ss -ti 2>/dev/null | head -n 80
cmd "/proc/net/snmp (TCP summary)" grep -A1 '^Tcp:' /proc/net/snmp 2>/dev/null
cmd "TCP retrans stats"          grep -A1 '^Tcp:' /proc/net/snmp 2>/dev/null | tail -n 1 | awk '{print "RetransSegs="$13}'
cmd "nstat TCP summary"          nstat -az Tcp_RetransSegs Tcp_InErrs Tcp_OutRsts Tcp_ActiveOpens Tcp_PassiveOpens 2>/dev/null

# ===========================================================================
# 14. STORAGE: BLOCK DEVICES
# ===========================================================================
hdr "14. STORAGE: BLOCK DEVICES"
cmd "lsblk (key cols)"           lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL,SERIAL,STATE,ROTA,DISC-GRAN,DISC-MAX
cmd "lsblk -t"                   lsblk -t
cmd "lsblk -O (everything, capped)" lsblk -O | head -n 200
cmd "blkid"                      blkid 2>/dev/null
cmd "/proc/partitions"           cat /proc/partitions
cmd "fdisk -l"                   fdisk -l 2>/dev/null
cmd "parted -l"                  parted -l 2>/dev/null

cmdsh "block device queue settings (per device)" <<'EOF'
for d in $(ls /sys/block/ 2>/dev/null | grep -vE '^(loop|ram|md|dm-|sr|fd)'); do
    q="/sys/block/$d/queue"
    [ -d "$q" ] || continue
    printf '----- %s -----\n' "$d"
    for k in scheduler rotational nr_requests read_ahead_kb max_sectors_kb max_hw_sectors_kb \
             logical_block_size physical_block_size discard_granularity discard_max_hw_bytes \
             rq_affinity nomerges wbt_lat_usec write_cache stable_writes io_poll io_poll_delay \
             fua rot_stable_writes add_random zoned dma_alignment iostats;
    do
        f="$q/$k"
        [ -r "$f" ] && printf '  %-22s = %s\n' "$k" "$(cat "$f" 2>/dev/null)"
    done
    # NVMe-specific
    for f in /sys/block/$d/device/model /sys/block/$d/device/serial /sys/block/$d/device/transport /sys/block/$d/device/queue_count; do
        [ -r "$f" ] && printf '  %s = %s\n' "$(echo "$f" | sed "s|/sys/block/$d/device/||")" "$(cat "$f")"
    done
done
EOF

cmd "df -h (filesystem usage)"   df -h
cmd "df -i (inode usage)"        df -i

# ===========================================================================
# 15. STORAGE: MOUNTS & FILESYSTEMS
# ===========================================================================
hdr "15. STORAGE: MOUNTS & FILESYSTEMS"
cmd "mount (filtered)"           mount | grep -vE 'proc|sysfs|cgroup|tmpfs|devpts|mqueue|pstore|bpf|tracefs|debugfs|configfs|fusectl|securityfs|hugetlbfs|autofs|systemd|mtd|rpc_pipefs|binfmt'
cmd "findmnt (tree)"             findmnt -t nfs,nfs4,zfs,ext4,xfs,btrfs 2>/dev/null
filecat "/etc/fstab"             /etc/fstab
cmdsh "ext4 reserved block % (per fs)" <<'EOF'
for dev in $(awk '$3=="ext4"{print $1}' /proc/mounts 2>/dev/null); do
    echo "=== $dev ==="
    dumpe2fs -h "$dev" 2>/dev/null | grep -E 'Reserved|Block count|Free blocks|Block size|Mount count|Maximum mount|Last'
done
EOF
cmdsh "xfs info (per fs)" <<'EOF'
for mp in $(awk '$3=="xfs"{print $2}' /proc/mounts 2>/dev/null); do
    echo "=== $mp ==="
    xfs_info "$mp" 2>/dev/null
done
EOF

# ===========================================================================
# 16. STORAGE: NFS (client + server) - critical for this cluster
# ===========================================================================
hdr "16. STORAGE: NFS (CLIENT + SERVER)"
cmd "nfsstat -m (mount options)"  nfsstat -m 2>/dev/null
cmd "nfsstat -c (client stats)"   nfsstat -c 2>/dev/null
cmd "nfsstat -s (server stats)"   nfsstat -s 2>/dev/null
cmd "nfsstat -234 (version stats)" nfsstat -234 2>/dev/null
cmd "showmount -e (exports)"      showmount -e 2>/dev/null
cmd "/proc/net/rpc/nfs"           cat /proc/net/rpc/nfs 2>/dev/null | head -n 60
cmd "/proc/net/rpc/nfsd"          cat /proc/net/rpc/nfsd 2>/dev/null | head -n 60
filecat "exports"                 /etc/exports /etc/exports.d/*.exports
filecat "nfs.conf"                /etc/nfs.conf /etc/nfs.conf.d/*.conf
filecat "nfs defaults"            /etc/default/nfs-kernel-server /etc/default/nfs-common
filecat "idmapd.conf"             /etc/idmapd.conf

cmd "pvesm status"                pvesm status
cmdsh "pvesm list per storage" <<'EOF'
for s in $(pvesm status 2>/dev/null | awk 'NR>1 && $3=="active"{print $1}'); do
    echo "--- storage: $s ---"
    pvesm list "$s" 2>/dev/null | head -n 30
    echo "--- pvesm config $s ---"
    pvesm config "$s" 2>/dev/null
done
EOF

# ===========================================================================
# 17. STORAGE: LVM
# ===========================================================================
hdr "17. STORAGE: LVM"
cmd "pvs"                        pvs -v
cmd "vgs"                        vgs -v
cmd "lvs"                        lvs -o +lv_layout,role,pool_lv,data_percent,metadata_percent --units b
cmd "pvdisplay"                  pvdisplay
cmd "vgdisplay"                  vgdisplay
cmd "lvdisplay"                  lvdisplay
filecat "lvm/lvm.conf (filters only)" /etc/lvm/lvm.conf
cmdsh "lvm.conf active filter" <<'EOF'
grep -E '^\s*(filter|global_filter|use_devicesfile|obtain_device_list_from_udev)' /etc/lvm/lvm.conf 2>/dev/null
EOF

# ===========================================================================
# 18. STORAGE: MULTIPATH
# ===========================================================================
hdr "18. STORAGE: MULTIPATH"
cmd "multipath -ll"              multipath -ll
cmd "multipath -v3 -ll (verbose, capped)" multipath -v3 -ll 2>/dev/null | head -n 100
filecat "multipath.conf"         /etc/multipath.conf
cmd "systemctl is-active multipathd"  systemctl is-active multipathd 2>/dev/null

# ===========================================================================
# 19. STORAGE: ZFS
# ===========================================================================
hdr "19. STORAGE: ZFS"
if command -v zpool >/dev/null 2>&1; then
    cmd "zfs version (userland)"   zfs version 2>/dev/null
    cmd "zpool version"            zpool version 2>/dev/null
    cmd "zpool list -v"            zpool list -v
    cmd "zpool status -v"          zpool status -v
    cmdsh "zpool key properties" <<'EOF'
zpool get name,size,alloc,free,expandsize,fragmentation,capacity,health,ashift,autotrim,autoreplace,cachefile,comment,listsnapshots,multihost,version,bootfs,delegation,failmode,freeing,leaked,readonly
EOF
    cmdsh "zfs per-dataset key properties" <<'EOF'
zfs get -r -o name,property,value,source \
    compression,atime,recordsize,volblocksize,sync,primarycache,secondarycache,checksum,xattr,acltype,aclinherit,copies,logbias,refreservation,reservation,quota,used,avail,refer,mountpoint,mounted,canmount,relative_path,snapdir,version,primarycache,redundant_metadata,encryption 2>/dev/null
EOF
    cmd "ARC stats"                cat /proc/spl/kstat/zfs/arcstats
    cmd "dmu_tx"                   cat /proc/spl/kstat/zfs/dmu_tx 2>/dev/null
    cmd "zfetchstats"              cat /proc/spl/kstat/zfs/zfetchstats 2>/dev/null
    cmd "vdev_cache_stats"         cat /proc/spl/kstat/zfs/vdev_cache_stats 2>/dev/null
    cmd "abdstats"                 cat /proc/spl/kstat/zfs/abdstats 2>/dev/null
    cmdsh "ZFS module parameters (perf-relevant)" <<'EOF'
for f in /sys/module/zfs/parameters/*; do
    [ -r "$f" ] || continue
    name="$(basename "$f")"
    case "$name" in
        zfs_arc_*|zfs_abd_*|zfs_dbuf_*|zfs_txg_*|zfs_vdev_*|zfs_sync_*|zfs_dirty_*|zfs_*io_*|zvol_*|l2arc_*|zfs_prefetch_*|zfs_*max_active|zfs_*_taskq*) ;;
        *) continue ;;
    esac
    printf '  %-50s = %s\n' "$name" "$(cat "$f")"
done
EOF
    cmd "zpool iostat snapshot"    zpool iostat 1 1 2>/dev/null
    cmd "arc_summary (if present)" arc_summary 2>/dev/null | head -n 80
    cmd "zpool events -v (recent)"  zpool events -v 2>/dev/null | tail -n 50
    cmdsh "zpool history (last 30 events)" <<'EOF'
zpool history -il 2>/dev/null | tail -n 30
EOF
else
    printf '\nZFS not present on this host.\n' >> "$OUT"
fi

# ===========================================================================
# 20. STORAGE: DRIVE HEALTH (SMART / NVMe)
# ===========================================================================
hdr "20. STORAGE: DRIVE HEALTH"
cmd "nvme list"                  nvme list 2>/dev/null
cmdsh "nvme smart-log per device" <<'EOF'
for n in /dev/nvme[0-9]*; do
    [ -b "$n" ] || continue
    echo "--- $n smart-log ---"
    nvme smart-log "$n" 2>&1
    echo "--- $n error-log (first 5) ---"
    nvme error-log "$n" 2>&1 | head -n 80
done
EOF
cmdsh "nvme id-ctrl per device (key fields)" <<'EOF'
for n in /dev/nvme[0-9]*; do
    [ -b "$n" ] || continue
    echo "--- $n id-ctrl (filtered) ---"
    nvme id-ctrl "$n" 2>&1 | grep -E '^(mn|sn|fn|fr|tnvmcap|vid|IEEE|cntlid|ver|rab|lpa|elpe|npdg|mdts|hmpre|hmmin|cap|frmw|lba|ws|awun|awupf|acwu|apsta)'
done
EOF
if command -v smartctl >/dev/null 2>&1; then
    cmdsh "smartctl -a per disk" <<'EOF'
for d in $(ls /sys/block/ 2>/dev/null | grep -E '^(sd|vd)'); do
    dev="/dev/$d"
    [ -b "$dev" ] || continue
    echo "--- smartctl -a $dev ---"
    smartctl -a "$dev" 2>&1 | head -n 200
done
EOF
fi

# ===========================================================================
# 21. STORAGE: HBA / RAID CONTROLLERS
# ===========================================================================
hdr "21. STORAGE: HBA / RAID CONTROLLERS"
for cli in storcli64 storcli MegaCli64 MegaCli perccli; do
    if command -v "$cli" >/dev/null 2>&1; then
        printf '\n--- %s found ---\n' "$cli" >> "$OUT"
        case "$cli" in
            storcli*|perccli)
                cmd "$cli /call show all"      "$cli" /call show all
                cmd "$cli /call/vall show all" "$cli" /call/vall show all 2>/dev/null
                ;;
            MegaCli*)
                cmd "MegaCli adapter info"     "$cli" -AdpAllInfo -aALL
                cmd "MegaCli physical drives"  "$cli" -PDList -aALL
                cmd "MegaCli logical drives"   "$cli" -LDInfo -Lall -aALL
                cmd "MegaCli BBUs"             "$cli" -AdpBbuCmd -aALL
                ;;
        esac
        break
    fi
done
for cli in sas2ircu sas3ircu; do
    if command -v "$cli" >/dev/null 2>&1; then
        cmd "$cli list"    "$cli" list
        cmd "$cli 0 display" "$cli" 0 display 2>/dev/null
    fi
done
command -v arcconf >/dev/null 2>&1 && cmd "arcconf list" arcconf list
command -v ssacli >/dev/null 2>&1 && cmd "ssacli ctrl all show config" ssacli ctrl all show config
command -v hpssacli >/dev/null 2>&1 && cmd "hpssacli ctrl all show config" hpssacli ctrl all show config

# ===========================================================================
# 22. VIRTUAL MACHINES (qm)
# ===========================================================================
hdr "22. VIRTUAL MACHINES (qm)"
cmd "qm list"                    qm list
cmdsh "qm config per VM" <<'EOF'
for vmid in $(qm list 2>/dev/null | awk 'NR>1{print $1}'); do
    echo "--- VM $vmid ---"
    qm config "$vmid" 2>&1
    echo ""
done
EOF
cmdsh "VM runtime status (qm guest agent if running)" <<'EOF'
for vmid in $(qm list 2>/dev/null | awk 'NR>1{print $1}'); do
    echo "--- VM $vmid runtime ---"
    qm guest cmd "$vmid" network-get-interfaces 2>/dev/null | head -n 40 || echo "  [no agent or VM not running]"
done
EOF
dirlist "/etc/pve/qemu-server"   /etc/pve/qemu-server
dirlist "/etc/pve/firewall"      /etc/pve/firewall

# ===========================================================================
# 23. CONTAINERS (pct)
# ===========================================================================
hdr "23. CONTAINERS (pct)"
cmd "pct list"                   pct list
cmdsh "pct config per CT" <<'EOF'
for ctid in $(pct list 2>/dev/null | awk 'NR>1{print $1}'); do
    echo "--- CT $ctid ---"
    pct config "$ctid" 2>&1
    echo ""
done
EOF

# ===========================================================================
# 24. SERVICES / FAILED UNITS
# ===========================================================================
hdr "24. SERVICES / FAILED UNITS"
cmd "running services"           systemctl list-units --type=service --state=running --no-pager
cmd "failed services"            systemctl --failed --no-pager
cmd "enabled services"           systemctl list-unit-files --state=enabled --no-pager | head -n 80
cmdsh "pve-* service status" <<'EOF'
systemctl status pve-cluster pvedaemon pveproxy pve-firewall pve-ha-crm pve-ha-lrm pvestatd pvebanner --no-pager 2>&1 | head -n 120
EOF
cmdsh "storage-related service status" <<'EOF'
systemctl status nfs-kernel-server nfs-server rpcbind rpc-statd zfs-import zfs-mount zfs-target smartd nvme-stat multipathd --no-pager 2>&1 | head -n 80
EOF

# ===========================================================================
# 25. TIMERS / CRON (could cause load spikes during work hours)
# ===========================================================================
hdr "25. TIMERS / CRON"
cmd "systemctl list-timers"      systemctl list-timers --all --no-pager | head -n 40
dirlist "/etc/cron.d"            /etc/cron.d
dirlist "/etc/cron.daily"        /etc/cron.daily
dirlist "/etc/cron.hourly"       /etc/cron.hourly
dirlist "/etc/cron.weekly"       /etc/cron.weekly
filecat "crontabs"               /etc/crontab
cmdsh "root crontab" <<'EOF'
crontab -l 2>/dev/null || echo "  [no root crontab]"
EOF

# ===========================================================================
# 26. LIVE PERFORMANCE SNAPSHOT
# ===========================================================================
hdr "26. LIVE PERFORMANCE SNAPSHOT"
cmd "/proc/loadavg"              cat /proc/loadavg
cmd "vmstat 1 3"                 vmstat 1 3
cmd "mpstat 1 3 (per-cpu)"       mpstat 1 3 2>/dev/null
cmd "iostat -xz 1 3"             iostat -xz 1 3 2>/dev/null
cmd "sar -dp 1 2"                sar -dp 1 2 2>/dev/null
cmd "sar -n DEV 1 2"             sar -n DEV 1 2 2>/dev/null
cmd "sar -n NFS,NFSD 1 2"        sar -n NFS,NFSD 1 2 2>/dev/null
cmd "sar -n SOCK 1 1"            sar -n SOCK 1 1 2>/dev/null
cmd "sar -n TCP,ETCP 1 2"        sar -n TCP,ETCP 1 2 2>/dev/null
cmd "pidstat -d 1 2 (top disk)"  pidstat -d 1 2 2>/dev/null | head -n 40
cmd "pidstat -u 1 2 (top cpu)"   pidstat -u 1 2 2>/dev/null | head -n 40
cmd "top (single snapshot, top 30)" top -b -n 1 | head -n 35
cmd "zpool iostat 1 2"           zpool iostat 1 2 2>/dev/null
cmd "arcstat 1 2"                arcstat 1 2 2>/dev/null

# /proc/net/dev snapshot twice for delta
cmdsh "/proc/net/dev delta (1s)" <<'EOF'
awk '{print $1}' /proc/net/dev
echo "--- t0 ---"
cat /proc/net/dev
sleep 1
echo "--- t1 (1s later) ---"
cat /proc/net/dev
EOF

# ===========================================================================
# 27. DNS / NTP / TIME
# ===========================================================================
hdr "27. DNS / NTP / TIME"
filecat "/etc/resolv.conf"       /etc/resolv.conf
filecat "/etc/hosts"             /etc/hosts
filecat "/etc/ntp.conf"          /etc/ntp.conf 2>/dev/null
filecat "/etc/chrony/chrony.conf" /etc/chrony/chrony.conf 2>/dev/null
cmd "timedatectl"                timedatectl 2>/dev/null
cmd "chronyc tracking"           chronyc tracking 2>/dev/null
cmd "chronyc sources"            chronyc sources 2>/dev/null
cmd "ntpq peers"                 ntpq -pn 2>/dev/null

# ===========================================================================
# 28. FIREWALL
# ===========================================================================
hdr "28. FIREWALL / SECURITY"
cmd "pve-firewall status"        pve-firewall status 2>/dev/null
cmd "nft ruleset (count)"        nft list ruleset 2>/dev/null | wc -l
cmd "nft ruleset (head)"         nft list ruleset 2>/dev/null | head -n 60
cmd "iptables-save (legacy)"     iptables-save 2>/dev/null | head -n 60
cmd "ipset list (names)"         ipset list -n 2>/dev/null
cmd "apparmor status"            aa-status 2>/dev/null | head -n 20

# ===========================================================================
# 29. TOP CONSUMERS (memory + disk + file handles)
# ===========================================================================
hdr "29. TOP CONSUMERS"
cmdsh "top 10 memory hogs" <<'EOF'
ps -eo pid,user,rss,vsz,pcpu,pmem,comm --sort=-rss | head -n 11
EOF
cmdsh "top 10 CPU hogs" <<'EOF'
ps -eo pid,user,pcpu,pmem,rss,comm --sort=-pcpu | head -n 11
EOF
cmdsh "top file-handle holders" <<'EOF'
for pid in $(ps -e -o pid= | head -n 200); do
    n="$(ls /proc/$pid/fd 2>/dev/null | wc -l)"
    [ "$n" -gt 50 ] && printf '%6d fd=%-6s %s\n' "$pid" "$n" "$(cat /proc/$pid/comm 2>/dev/null)"
done | sort -k2 -t= -nr | head -n 15
EOF
cmdsh "largest files in /var/log (top 10)" <<'EOF'
find /var/log -type f -size +10M -exec ls -lh {} \; 2>/dev/null | sort -k5 -h -r | head -n 10
EOF

# ===========================================================================
# 30. QUICK-LOOK SUSPECT SUMMARY (auto-detect common issues)
# ===========================================================================
hdr "30. QUICK-LOOK SUSPECT SUMMARY"
blank
printf 'Auto-detected performance anti-patterns (verify against full data above):\n' >> "$OUT"
blank

cmdsh "[A] Network interfaces < 10 Gbps and up" <<'EOF'
hits=0
for ifc in $NETIFS; do
    s=$(cat "/sys/class/net/$ifc/speed" 2>/dev/null)
    c=$(cat "/sys/class/net/$ifc/carrier" 2>/dev/null)
    case "$s" in
        10|100|1000)
            if [ "$c" = "1" ]; then
                printf '  %s : %s Mbps (UP)  <-- sub-10GbE; suspect if this carries storage/migration\n' "$ifc" "$s"
                hits=$((hits+1))
            fi
            ;;
    esac
done
[ "$hits" = "0" ] && echo "  no <10GbE interfaces are UP (or speed unreadable)"
EOF

blank
cmdsh "[B] MTU not jumbo on carrier-up interfaces" <<'EOF'
hits=0
for ifc in $NETIFS; do
    m=$(cat "/sys/class/net/$ifc/mtu" 2>/dev/null)
    c=$(cat "/sys/class/net/$ifc/carrier" 2>/dev/null)
    [ "$c" = "1" ] || continue
    if [ -n "$m" ] && [ "$m" != "9000" ]; then
        printf '  %s : mtu=%s\n' "$ifc" "$m"
        hits=$((hits+1))
    fi
done
[ "$hits" = "0" ] && echo "  all carrier-up interfaces have MTU 9000 (or none are up)"
EOF

blank
cmdsh "[C] CPU governor != performance" <<'EOF'
g=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null)
if [ -z "$g" ]; then
    echo "  cpufreq driver not loaded (governor unknown)"
elif [ "$g" = "performance" ]; then
    echo "  OK: performance governor"
else
    echo "  <-- governor=$g  (recommend 'performance')"
fi
EOF

blank
cmdsh "[D] EPB / EPP not performance-bias" <<'EOF'
epb=$(cat /sys/devices/system/cpu/cpu0/power/energy_perf_bias 2>/dev/null)
epp=$(cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference 2>/dev/null)
echo "  EPB=$epb  EPP=$epp  (0=performance, 15=max-power on EPB; 'performance' on EPP)"
EOF

blank
cmdsh "[E] vm.swappiness and dirty ratios" <<'EOF'
sw=$(sysctl -n vm.swappiness 2>/dev/null)
dr=$(sysctl -n vm.dirty_ratio 2>/dev/null)
dbr=$(sysctl -n vm.dirty_background_ratio 2>/dev/null)
echo "  swappiness=$sw  dirty_ratio=$dr  dirty_background_ratio=$dbr"
[ "$sw" != "10" ] && [ -n "$sw" ] && echo "    <-- swappiness not 10 (Proxmox default)"
EOF

blank
cmdsh "[F] TCP congestion control" <<'EOF'
cc=$(sysctl -n net.ipv4.tcp_congestion_control 2>/dev/null)
avail=$(sysctl -n net.ipv4.tcp_available_congestion_control 2>/dev/null)
echo "  active: $cc"
echo "  available: $avail"
[ "$cc" = "cubic" ] && echo "    <-- consider bbr (if available) for high-BDP NFS paths"
EOF

blank
cmdsh "[G] ZFS dataset red flags" <<'EOF'
if ! command -v zfs >/dev/null 2>&1; then
    echo "  ZFS not present"
else
    zfs get -r -o name,property,value compression,atime,sync,recordsize,xattr 2>/dev/null \
      | awk '$2=="compression" && $3=="off" {print "  <-- "$1" has compression=off"}
             $2=="atime" && $3=="on"        {print "  <-- "$1" has atime=on"}
             $2=="sync" && $3=="always"     {print "  <-- "$1" has sync=always (slow without SLOG)"}
             $2=="xattr" && $3=="on"        {print "  <-- "$1" has xattr=on (use sa)"}'
fi
EOF

blank
cmdsh "[H] ARC size vs RAM" <<'EOF'
if [ -r /proc/spl/kstat/zfs/arcstats ]; then
    arc=$(awk '/^size /{print $3}' /proc/spl/kstat/zfs/arcstats 2>/dev/null)
    cmax=$(awk '/^c_max /{print $3}' /proc/spl/kstat/zfs/arcstats 2>/dev/null)
    ram=$(awk '/MemTotal/{print $2*1024}' /proc/meminfo)
    echo "  ARC current=$arc bytes  ARC max=$cmax bytes  RAM total=$ram bytes"
    if [ -n "$cmax" ] && [ -n "$ram" ] && [ "$ram" -gt 0 ]; then
        pct=$((cmax * 100 / ram))
        echo "  ARC max is ${pct}% of RAM (Proxmox default ~50%)"
    fi
    hits=$(awk '/^hits /{print $3}' /proc/spl/kstat/zfs/arcstats 2>/dev/null)
    miss=$(awk '/^misses /{print $3}' /proc/spl/kstat/zfs/arcstats 2>/dev/null)
    if [ -n "$hits" ] && [ -n "$miss" ] && [ "$((hits+miss))" -gt 0 ]; then
        hr=$((hits * 100 / (hits + miss)))
        echo "  ARC hit rate since boot: ${hr}% (hits=$hits misses=$miss)"
    fi
else
    echo "  ZFS ARC stats not available (ZFS not loaded?)"
fi
EOF

blank
cmdsh "[I] VMs with non-optimal storage/NIC models" <<'EOF'
hits=0
for vmid in $(qm list 2>/dev/null | awk 'NR>1{print $1}'); do
    cfg=$(qm config "$vmid" 2>/dev/null)
    flags=""
    echo "$cfg" | grep -qE '^ide[0-9]'                 && flags="${flags}ide-disk "
    echo "$cfg" | grep -qE '^sata[0-9]'                && flags="${flags}sata-disk "
    echo "$cfg" | grep -qE '^scsi[0-9].*:,'            && echo "$cfg" | grep -E '^scsi[0-9]' | grep -qv 'virtio-scsi' && flags="${flags}scsi-no-virtio-scsi "
    echo "$cfg" | grep -qE '^scsihw: ' && ! echo "$cfg" | grep -qE '^scsihw: virtio-scsi-single' && flags="${flags}scsihw!=virtio-scsi-single "
    echo "$cfg" | grep -qE '^cpu: .*kvm64'             && flags="${flags}cpu=kvm64 "
    echo "$cfg" | grep -q '^iothread: 1'               || echo "$cfg" | grep -qE '^scsi[0-9].*iothread=1' || flags="${flags}no-iothread "
    echo "$cfg" | grep -qE '^net[0-9].*model=(e1000|rtl8139|vmxnet)' && flags="${flags}legacy-nic "
    echo "$cfg" | grep -qE '^net[0-9].*model=virtio' && ! echo "$cfg" | grep -qE 'multiqueue=|queues=' && flags="${flags}virtio-no-multiqueue "
    if [ -n "$flags" ]; then
        echo "  VM $vmid: $flags"
        hits=$((hits+1))
    fi
done
[ "$hits" = "0" ] && echo "  no obvious VM storage/NIC anti-patterns"
EOF

blank
cmdsh "[J] NFS client mount option red flags" <<'EOF'
if ! command -v nfsstat >/dev/null 2>&1; then
    echo "  nfsstat not available"
else
    nfsstat -m 2>/dev/null | awk '
        /^.*:/ {mp=$0; next}
        /Flags:/ {
            gsub(/Flags:|,/,"",$0)
            line=$0
            if (line !~ /noatime/)        print "  "mp" : missing noatime"
            if (line !~ /nconnect/)       print "  "mp" : missing nconnect (kernel >=5.3)"
            if (line !~ /rsize=1048576/)  print "  "mp" : rsize<1M"
            if (line !~ /wsize=1048576/)  print "  "mp" : wsize<1M"
            if (line ~ /sync/)            print "  "mp" : sync mount (slow)"
        }
    '
fi
EOF

blank
cmdsh "[K] Softnet / NIC drops (non-zero)" <<'EOF'
awk 'NR<=32 && ($2+0 > 0 || $4+0 > 0) {printf "  cpu%d: dropped=%d time_squeezed=%d\n", NR-1, $2, $4}' /proc/net/softnet_stat
echo "  (also check ethtool -S sections above for rx_dropped/rx_missed_errors)"
EOF

blank
cmdsh "[L] Failed systemd services" <<'EOF'
failed=$(systemctl --failed --no-legend 2>/dev/null | head -n 10)
if [ -n "$failed" ]; then
    echo "$failed" | sed 's/^/  /'
else
    echo "  no failed services"
fi
EOF

blank
cmdsh "[M] Filesystem usage > 80%" <<'EOF'
df -h 2>/dev/null | awk 'NR>1 && $5+0 > 80 {print "  "$0}'
EOF

blank
cmdsh "[N] SMART / NVMe health warnings" <<'EOF'
if command -v nvme >/dev/null 2>&1; then
    for n in /dev/nvme[0-9]*; do
        [ -b "$n" ] || continue
        crit=$(nvme smart-log "$n" 2>/dev/null | grep -iE 'critical_warning|media_and_data_integrity|available_spare' | head -n 5)
        [ -n "$crit" ] && echo "  $n: $crit" | tr '\n' ' '
    done
fi
if command -v smartctl >/dev/null 2>&1; then
    for d in $(ls /sys/block/ 2>/dev/null | grep -E '^sd'); do
        dev="/dev/$d"
        [ -b "$dev" ] || continue
        chk=$(smartctl -H "$dev" 2>/dev/null | grep -iE 'result|health|passed|failed')
        echo "$chk" | grep -qi fail && echo "  $dev: $chk"
    done
fi
echo "  (see section 20 for full smartctl/nvme output)"
EOF

# ===========================================================================
# Footer
# ===========================================================================
END_EPOCH="$(date +%s)"
DUR=$((END_EPOCH - START_EPOCH))
BYTES="$(wc -c < "$OUT")"
LINES="$(wc -l < "$OUT")"
{
    printf '\n###############################################################################\n'
    printf '# Collection complete: %s\n' "$HOST"
    printf '# Output:  %s\n' "$OUT"
    printf '# Size:    %s bytes / %s lines\n' "$BYTES" "$LINES"
    printf '# Elapsed: %s seconds\n' "$DUR"
    printf '###############################################################################\n'
} >> "$OUT"

chmod 600 "$OUT"
echo
echo "================================================================"
echo " check.sh done. Wrote: ${OUT}"
echo " Size:    $(du -h "$OUT" | cut -f1)  (${LINES} lines)"
echo " Elapsed: ${DUR}s"
echo " Please copy ${OUT} back for analysis."
echo "================================================================"
