#!/usr/bin/env bash ############################################################################### # lacp-rx-distribution.sh (HOST-NATIVE version) # # Runs ON a host (tsys6 or tsys7). Auto-detects role by hostname: # - On tsys6 (receiver): starts iperf3 -s -1 (one-shot), snapshots local # bond0 slave RX counters before+after, writes verdict + log. # - On tsys7 (sender): snapshots local bond0 slave TX counters before+ # after, runs iperf3 -c -P 8 -t 12. # # Each side writes only its own counters. No inter-host SSH required. # iperf3 -s -1 (one-shot) handles client/server coordination. # # Output: /root/lacp-rx-{receiver,sender}.log ############################################################################### set -uo pipefail DURATION="${LACP_TEST_SECS:-12}" STREAMS="${LACP_STREAMS:-8}" PEER_RX_IP="10.100.100.6" # tsys6 storage IP (receiver) PEER_TX_IP="10.100.100.7" # tsys7 storage IP (sender) LOG_DIR="/root" BOND="bond0" # Snapshot per-slave byte counters from $BOND. Output: " rx tx\n" sorted. snapshot() { awk '/^Slave Interface:/{print $3}' "/proc/net/bonding/$BOND" | while read -r s; do [ -n "$s" ] || continue rx=$(cat "/sys/class/net/$s/statistics/rx_bytes" 2>/dev/null || echo 0) tx=$(cat "/sys/class/net/$s/statistics/tx_bytes" 2>/dev/null || echo 0) printf '%s %s %s\n' "$s" "$rx" "$tx" done | sort } # Compute per-slave deltas. $1=label, $2=col(2=rx,3=tx), $3=before, $4=after. analyze() { local label="$1" col="$2" before="$3" after="$4" join "$before" "$after" | awk -v col="$col" -v lbl="$label" ' { slave=$1 b = (col==2 ? $2 : $3)+0 a = (col==2 ? $4 : $5)+0 d = a - b; if (d < 0) d = 0 delta[slave]=d; bef[slave]=b; aft[slave]=a order[++n]=slave; total+=d } END { printf "\n--- %s (bytes) ---\n", lbl printf " %-12s %14s %14s %14s %8s\n", "SLAVE", "BEFORE", "AFTER", "DELTA", "PCT" for (i=1;i<=n;i++){ s=order[i] pct = (total>0 ? 100*delta[s]/total : 0) printf " %-12s %14d %14d %14d %7.1f%%\n", s, bef[s], aft[s], delta[s], pct } printf " %-12s %14s %14s %14d %8s\n", "TOTAL", "", "", total, "100.0%" }' } # Dominant-slave pct for the verdict line. $1=col, $2=before, $3=after. dominant() { join "$2" "$3" | awk -v col="$1" ' { b=(col==2?$2:$3)+0; a=(col==2?$4:$5)+0; d=a-b; if(d<0)d=0; tot+=d; delta[$1]=d } END { m=0; ms=""; for (s in delta){ if (delta[s]>m){m=delta[s]; ms=s} } printf "%.1f %s", (tot>0?100*m/tot:0), ms }' } HOST="$(uname -n)" HOST="${HOST%%.*}" case "$HOST" in *tsys6) ROLE="receiver"; LOG="$LOG_DIR/lacp-rx-receiver.log" ;; *tsys7) ROLE="sender"; LOG="$LOG_DIR/lacp-rx-sender.log" ;; *) echo "ERROR: not running on tsys6 or tsys7 (hostname=$HOST)"; exit 2 ;; esac TMP="$(mktemp -d)" trap 'rm -rf "$TMP"' EXIT { echo "=== LACP per-slave distribution ($ROLE) ===" echo "Host: $HOST Role: $ROLE" echo "Date: $(date -u +%Y-%m-%dT%H:%M:%SZ)" echo "Bond: $BOND" echo "" echo "--- $BOND state ---" grep -E "Bonding Mode|Transmit Hash|Number of ports|Partner Mac|Slave Interface|Link" \ "/proc/net/bonding/$BOND" 2>&1 echo "" } | tee "$LOG" snapshot > "$TMP/before" if [ "$ROLE" = "receiver" ]; then echo "[receiver] starting one-shot iperf3 server on $PEER_RX_IP ..." pkill -x iperf3 2>/dev/null; sleep 0.5 nohup iperf3 -s -1 -B "$PEER_RX_IP" > "$TMP/iperf.out" 2>&1 & SRV_PID=$! # Wait for the server to be ready (brief), then wait for it to exit # (one-shot server exits after serving one client). for _ in $(seq 1 60); do kill -0 "$SRV_PID" 2>/dev/null || break sleep 1 done wait "$SRV_PID" 2>/dev/null echo "[receiver] iperf3 server finished." else # sender: wait briefly for receiver to be listening, then run client. echo "[sender] waiting 3s for receiver to listen, then running iperf3 client..." sleep 3 iperf3 -c "$PEER_RX_IP" -B "$PEER_TX_IP" -P "$STREAMS" -t "$DURATION" -l 128k -O 2 \ > "$TMP/iperf.out" 2>&1 echo "[sender] iperf3 client finished (exit=$?)." fi snapshot > "$TMP/after" { echo "" echo "--- iperf3 output (raw tail) ---" tail -25 "$TMP/iperf.out" echo "" if [ "$ROLE" = "receiver" ]; then analyze "Receiver RX (THE key column)" 2 "$TMP/before" "$TMP/after" dom="$(dominant 2 "$TMP/before" "$TMP/after")" pct=${dom%% *}; top=${dom##* } echo "" echo "===================================================================" if awk -v p="$pct" 'BEGIN{exit !(p>=90)}'; then echo " VERDICT: SWITCH NOT distributing across $HOST's two ports" echo " Dominant slave '$top' = ${pct}% of RX -> hash not effective" echo " on this LAG. Action: verify/bounce pfv-r3-tor-stor port-channel." else echo " VERDICT: switch IS distributing (top slave '$top' = ${pct}%)." echo " Cap is host-side: softirq/CPU/bridge/NIC coalescing." fi echo "===================================================================" else analyze "Sender TX (control: should split if host hash=layer3+4)" 3 "$TMP/before" "$TMP/after" dom="$(dominant 3 "$TMP/before" "$TMP/after")" pct=${dom%% *}; top=${dom##* } echo "" echo "===================================================================" if awk -v p="$pct" 'BEGIN{exit !(p>=90)}'; then echo " VERDICT: host TX NOT distributing (top slave '$top' = ${pct}%)." echo " Host xmit_hash_policy is NOT effective despite /proc/net/bonding." else echo " VERDICT: host TX distributing OK (top slave '$top' = ${pct}%)." fi echo "===================================================================" fi } | tee -a "$LOG" echo "" echo "Log written: $LOG"