feat(traffic): Add SWB loopback line-speed traffic tool

Blanton_Script/blanton_traffic_linespeed.sh V0.3.2 drives the whole
loopback traffic run from the DUT shell via bcmcmd, for both switch
boards (unit 0 and 1):

- init  : vlan remove 1 pbm=cd, then create/add the 108 loopback
          pairs (VLAN 30..137, cdN <-> cdN+32)
- clear : clear c
- show  : show c (raw)
- start : tx <count> length=<len> VLantag=<vid> per VLAN
- stop  : remove the pairs, optional --destroy
- ps    : port status of the cabled ports
- report: parse the show c MIB_TPOK/MIB_RPOK counters and lay them
          out one pair per block with a cross-check verdict
          (cdA.TX == cdB.RX and cdA.RX == cdB.TX), PASS green /
          FAIL red / NA yellow, --tolerance for snapshot skew,
          --tsv, --raw and -f <log> for offline analysis; the raw
          capture is kept under /tmp
- run   : ps -> init -> clear -> start -> report

Options -u/-c/-l/-p/-T/-d/-n and a sourceable function API, in the
style of the other blanton_*.sh tools. Command output matches the
former Tera Term macros byte for byte; report output matches
tools/bcm_mibpair_report_V1.1.0.py, added here as the off-box
equivalent, together with the raw bcmcmd command lists under tools/.

ScriptA sources the tool, ScriptB runs ps/init/clear/show/start and
drops stress_hhmd and stress_pcie, ScriptC stops the traffic and
prints the pair counter report.
This commit is contained in:
2026-08-17 16:32:43 +08:00
parent 9bf4291d17
commit 071244fde1
8 changed files with 1624 additions and 27 deletions
+10 -1
View File
@@ -7,7 +7,14 @@
; Version History:
; V1.0.0 2026-08-13 Initial Version
; V1.0.1 2026-08-14 Add CLEAR PCIE Error
; V1.0.2 2026-08-17 utils/setup_pmon.ttl add Leakage Status
; V1.0.2 2026-08-17 utils/setup_pmon.ttl Add Leakage Status
; utils/traffic_loopback_vlan_setting.ttl Created
; utils/traffic_loopback_vlan_remove.ttl Created
; utils/traffic_loopback_start.ttl Created
; ScriptB Remove stress_hhmd, stress_pcie
; Blanton_Script Add blanton_traffic_linespeed.sh
; ScriptB/C Add Traffic Test
;
; =============================================================================
include "config.ttl"
@@ -52,6 +59,8 @@ sendln "source ~/Blanton_Script/blanton_icb_vi2c.sh"
wait prompt_sonic_root
sendln "source ~/Blanton_Script/blanton_pwr_data.sh"
wait prompt_sonic_root
sendln "source ~/Blanton_Script/blanton_traffic_linespeed.sh"
wait prompt_sonic_root
sendln "source ~/Blanton_Script/LTC2980_Margin_Script/margin.sh"
; ========== CLEAR PCIE Error ==========
+15 -16
View File
@@ -1,13 +1,5 @@
; =============================================================================
; Script B for Blanton
; Version : V1.0.2
; Date : 2026-08-17
; Author : ETWen
; =============================================================================
; Version History:
; V1.0.0 2026-08-13 Initial Version
; V1.0.1 2026-08-14 Add AMD CPU STRESS TEST
; V1.0.2 2026-08-17 utils/setup_pmon.ttl add Leakage Status
; =============================================================================
include "config.ttl"
@@ -40,18 +32,10 @@ sendln "python ~/hammer/tools/stress_mem.py &"
wait prompt_sonic_root
sendln "python ~/hammer/tools/stress_mem.py &"
; ========== I2C, LSB and PCIE read with unlimited loop ==========
wait prompt_sonic_root
sendln "python ~/hammer/tools/stress_hhmd.py &"
; ========== SSD read/write ==========
wait prompt_sonic_root
sendln "python ~/hammer/tools/stress_ssd.py &"
; ========== PCIe CPU <- PCIe -> ASIC by dumping portStatus ==========
wait prompt_sonic_root
sendln "python ~/hammer/tools/stress_pcie.py &"
; ========== USB read/write ==========
wait prompt_sonic_root
sendln "python ~/hammer/tools/stress_usb.py &"
@@ -62,6 +46,21 @@ sendln "jobs"
; ========== Take one round of eye measurement on uplink ports ==========
; ========== Traffic START ==========
wait prompt_sonic_root
sendln "blanton_traffic_linespeed ps -u 0"
wait prompt_sonic_root
sendln "blanton_traffic_linespeed init -u 0"
wait prompt_sonic_root
sendln "blanton_traffic_linespeed clear -u 0"
wait prompt_sonic_root
pause 3
sendln "blanton_traffic_linespeed show -u 0"
wait prompt_sonic_root
sendln "blanton_traffic_linespeed start -u 0"
; ========== Get data every 10mins ==========
while 1
include "utils/setup_pmon.ttl"
+4 -10
View File
@@ -1,13 +1,5 @@
; =============================================================================
; Script C for Blanton
; Version : V1.0.2
; Date : 2026-08-17
; Author : ETWen
; =============================================================================
; Version History:
; V1.0.0 2026-08-13 Initial Version
; V1.0.1 2026-08-14 Add Check PCIE error counters,CHECK Stress results
; V1.0.2 2026-08-17 utils/setup_pmon.ttl add Leakage Status
; =============================================================================
include "config.ttl"
@@ -40,8 +32,10 @@ wait prompt_sonic_root
sendln "cat ~/hammer/tools/log.stress_ssd"
; ========== CHECK Traffic counters ==========
;wait prompt_sonic_root
;sendln "command"
wait prompt_sonic_root
sendln "blanton_traffic_linespeed stop -u 0"
wait prompt_sonic_root
sendln "blanton_traffic_linespeed report -u 0"
messagebox 'GOOD JOB! Test Case DONE' 'teraterm'
@@ -0,0 +1,729 @@
#!/usr/bin/env bash
# =============================================================================
# blanton_traffic_linespeed.sh -- SWB loopback line-speed traffic via bcmcmd
# =============================================================================
# Version History:
# V0.1.0 2026-08-17 First version. Bash port of the three Tera Term macros
# utils/traffic_loopback_vlan_setting.ttl (VLAN 30..137,
# port pair cdN <-> cdN+32), utils/traffic_loopback_start.ttl
# (tx 100 length=512 VLantag=<vid>) and
# utils/traffic_loopback_vlan_remove.ttl.
# One DUT = two switch boards -> bcmcmd unit 0 and 1
# (bcmcmd -n <unit>), default both.
# V0.2.0 2026-08-17 Add: 'ps' (port status) subcommand -- 'ps <portlist>' with
# the cabled-port list as default ($TL_PS_PORTS), -p/--ports
# to override (or a bare port list after 'ps'), 'ps -p all'
# for every port. 'run' now shows port status first.
# V0.3.0 2026-08-17 Add: 'report' subcommand -- runs 'show c', keeps the raw
# log, and renders the MIB_TPOK/MIB_RPOK counters as a
# loopback-pair table with a PASS/FAIL cross-check
# (cdA.TX==cdB.RX and cdA.RX==cdB.TX), i.e. an on-DUT awk
# port of tools/bcm_mibpair_report_V1.1.0.py (same layout,
# same verdict rules, same summary lines, exit 2 on
# FAIL/NA). Options -T/-a/-g/-P/-q/--tsv/--raw and
# -f <log> to re-parse a captured console log offline.
# 'run' now ends with 'report' instead of a raw 'show c'.
# V0.3.1 2026-08-17 Fix: only the FIRST VLAN was configured -- bcmcmd inherited
# the while-loop's stdin (the pair-list here-string) and ate
# the remaining lines, so 'read' hit EOF after one pass.
# bcmcmd now always runs with </dev/null and the loops read
# on fd 3 (3<<< "$TL_PAIRS").
# Fix: bcmcmd exits 0 even when the BCM shell rejects the
# command ("VLAN: ERROR: Entry exists" / "Failed to execute
# the diagnostic command"), so failures were counted as OK.
# Output is now pattern-matched ($TL_ERR_PAT), with benign
# cases tolerated and counted separately: 'Entry exists' on
# 'vlan create' (re-run of init) and 'not found' on
# 'vlan remove/destroy' (stop before init).
# V0.3.2 2026-08-17 Add: report colours the verdict -- PASS green, FAIL red,
# NA yellow (also in the summary lines). Padding is applied
# to the plain text before colouring so the columns stay
# aligned. Auto-disabled when stdout is not a terminal or
# with --tsv; --color / --no-color / TL_COLOR force it.
# =============================================================================
#
# The VLAN/port table below is copied verbatim from
# utils/traffic_loopback_vlan_setting.ttl -- it is NOT generated, because the
# port map has gaps (cd15..19, cd47..51, cd79..83, cd111..115, cd139,
# cd148..151, cd171, cd180..183, cd203, cd212..215, cd235, cd244..247 are not
# cabled). Keep this file and the .ttl in sync if the loopback harness changes.
#
# Usage (run directly):
# ./blanton_traffic_linespeed.sh init # step 1: VLAN loopback setup
# ./blanton_traffic_linespeed.sh clear # step 2: clear c
# ./blanton_traffic_linespeed.sh show # step 3/6: show c
# ./blanton_traffic_linespeed.sh start # step 4: tx burst per VLAN
# ./blanton_traffic_linespeed.sh stop # step 5: remove VLAN members
# ./blanton_traffic_linespeed.sh ps # port status (link/speed) of
# # the cabled ports
# ./blanton_traffic_linespeed.sh report # step 6+: show c -> per-pair
# # TX/RX table + PASS/FAIL
# ./blanton_traffic_linespeed.sh run # ps -> 1 -> 2 -> 4 -> report
#
# Usage (sourced, like the other blanton_*.sh tools):
# source blanton_traffic_linespeed.sh
# blanton_traffic_linespeed init -u 1 -d
#
# Options (any order, after the subcommand):
# -u|--unit <0|1|all|0,1> bcmcmd unit(s). Default: $TL_UNITS ("0 1")
# -c|--count <n> tx packet count (default $TL_TX_COUNT)
# -l|--length <n> tx packet length (default $TL_TX_LENGTH)
# -p|--ports <list|all> ps: port list ('all' = plain 'ps', every port)
# -T|--tolerance <n> report: allowed |delta| in packets (default 0;
# -T 1 absorbs the normal snapshot skew)
# -a|--all report: also list pairs that have no counters
# -g|--grouped report: comma-group the values (1,234,567)
# -P|--with-port report: render cd1(2) instead of cd1
# -q|--quiet report: drop the trailing summary comments
# --tsv report: tab-separated (paste into Excel)
# --raw report: also print the raw 'show c' output
# --color|--no-color report: force / disable PASS-green FAIL-red colours
# (default: colour only when stdout is a terminal)
# -f|--file <log> report: parse a saved console log, do not touch DUT
# -d|--debug print every bcmcmd before running it
# -n|--dry-run print commands only, do not touch the DUT
# --destroy stop: also 'vlan destroy <vid>' after removing members
# -h|--help help
#
# Environment overrides:
# BCMCMD_CMD=bcmcmd TL_UNITS="0 1" TL_TX_COUNT=100 TL_TX_LENGTH=512
# TL_PS_PORTS="1-24,40-96,..." TL_TOLERANCE=0 TL_LOG_DIR=/tmp
# DEBUG_MODE=1
#
# Related: tools/bcm_mibpair_report_V1.1.0.py -- same report off-box (python3,
# richer options). 'report' is the awk equivalent so it runs on the SONiC DUT
# itself; both accept the same captured 'show c' log.
# =============================================================================
# === Config ==================================================================
BCMCMD_CMD="${BCMCMD_CMD:-bcmcmd}"
TL_UNITS="${TL_UNITS:-0 1}" # one DUT = two switch boards (0 and 1)
TL_TX_COUNT="${TL_TX_COUNT:-100}"
TL_TX_LENGTH="${TL_TX_LENGTH:-512}"
TL_DEFAULT_VLAN="${TL_DEFAULT_VLAN:-1}"
TL_DEFAULT_VLAN_PBM="${TL_DEFAULT_VLAN_PBM:-cd}"
# 'ps' port list: the cabled ports of the loopback harness, in bcm logical-port
# numbering (NOT the cdN names used by vlan add). Kept as one string so it can be
# pasted straight from/into a manual "bcmcmd -n 0 -c 'ps <list>'".
TL_PS_PORTS="${TL_PS_PORTS:-1-24,40-96,112-168,184-240,256-259,270-308,310-327,342-380,382-399,414-452,454-471,486-524,526-543,558-565}"
# --- report (pair cross-check) defaults -------------------------------------
TL_TOLERANCE="${TL_TOLERANCE:-0}" # allowed |delta| in packets before FAIL
TL_LOG_DIR="${TL_LOG_DIR:-/tmp}" # where the raw 'show c' capture is kept
TL_REPORT_ALL=${TL_REPORT_ALL:-0}
TL_REPORT_GROUPED=${TL_REPORT_GROUPED:-0}
TL_REPORT_WITHPORT=${TL_REPORT_WITHPORT:-0}
TL_REPORT_QUIET=${TL_REPORT_QUIET:-0}
TL_REPORT_TSV=${TL_REPORT_TSV:-0}
TL_REPORT_RAW=${TL_REPORT_RAW:-0}
TL_REPORT_FILE="" # -f <log>: parse this instead of the DUT
TL_COLOR="${TL_COLOR:-auto}" # auto|always|never -- PASS green / FAIL red / NA yellow
# Debug / dry-run (1 = on). DEBUG_MODE is shared with blanton_fpga_pcimem.sh.
DEBUG_MODE=${DEBUG_MODE:-0}
TL_DRY_RUN=${TL_DRY_RUN:-0}
TL_DESTROY=${TL_DESTROY:-0}
# === VLAN <-> loopback port pair table (from traffic_loopback_vlan_setting.ttl) ==
# format: <vid> <port_a> <port_b>
TL_PAIRS="\
30 cd0 cd32
31 cd1 cd33
32 cd2 cd34
33 cd3 cd35
34 cd4 cd36
35 cd5 cd37
36 cd6 cd38
37 cd7 cd39
38 cd8 cd40
39 cd9 cd41
40 cd10 cd42
41 cd11 cd43
42 cd12 cd44
43 cd13 cd45
44 cd14 cd46
45 cd20 cd52
46 cd21 cd53
47 cd22 cd54
48 cd23 cd55
49 cd24 cd56
50 cd25 cd57
51 cd26 cd58
52 cd27 cd59
53 cd28 cd60
54 cd29 cd61
55 cd30 cd62
56 cd31 cd63
57 cd64 cd96
58 cd65 cd97
59 cd66 cd98
60 cd67 cd99
61 cd68 cd100
62 cd69 cd101
63 cd70 cd102
64 cd71 cd103
65 cd72 cd104
66 cd73 cd105
67 cd74 cd106
68 cd75 cd107
69 cd76 cd108
70 cd77 cd109
71 cd78 cd110
72 cd84 cd116
73 cd85 cd117
74 cd86 cd118
75 cd87 cd119
76 cd88 cd120
77 cd89 cd121
78 cd90 cd122
79 cd91 cd123
80 cd92 cd124
81 cd93 cd125
82 cd94 cd126
83 cd95 cd127
84 cd128 cd160
85 cd129 cd161
86 cd130 cd162
87 cd131 cd163
88 cd132 cd164
89 cd133 cd165
90 cd134 cd166
91 cd135 cd167
92 cd136 cd168
93 cd137 cd169
94 cd138 cd170
95 cd140 cd172
96 cd141 cd173
97 cd142 cd174
98 cd143 cd175
99 cd144 cd176
100 cd145 cd177
101 cd146 cd178
102 cd147 cd179
103 cd152 cd184
104 cd153 cd185
105 cd154 cd186
106 cd155 cd187
107 cd156 cd188
108 cd157 cd189
109 cd158 cd190
110 cd159 cd191
111 cd192 cd224
112 cd193 cd225
113 cd194 cd226
114 cd195 cd227
115 cd196 cd228
116 cd197 cd229
117 cd198 cd230
118 cd199 cd231
119 cd200 cd232
120 cd201 cd233
121 cd202 cd234
122 cd204 cd236
123 cd205 cd237
124 cd206 cd238
125 cd207 cd239
126 cd208 cd240
127 cd209 cd241
128 cd210 cd242
129 cd211 cd243
130 cd216 cd248
131 cd217 cd249
132 cd218 cd250
133 cd219 cd251
134 cd220 cd252
135 cd221 cd253
136 cd222 cd254
137 cd223 cd255"
# === Logging =================================================================
_tl_info() { echo -e "[\033[34mINFO\033[0m] $*"; }
_tl_ok() { echo -e "[\033[32m OK \033[0m] $*"; }
_tl_warn() { echo -e "[\033[33mWARN\033[0m] $*" >&2; }
_tl_err() { echo -e "[\033[31mERR \033[0m] $*" >&2; }
_tl_dbg() { [ "$DEBUG_MODE" = "1" ] && echo -e "\033[90m[DEBG] $*\033[0m" >&2; return 0; }
traffic_debug() {
if [ "${1:-}" = "on" ]; then DEBUG_MODE=1
elif [ "${1:-}" = "off" ]; then DEBUG_MODE=0
else [ "$DEBUG_MODE" = "0" ] && DEBUG_MODE=1 || DEBUG_MODE=0; fi
_tl_info "DEBUG_MODE=$DEBUG_MODE"
}
# === bcmcmd wrapper ==========================================================
# IMPORTANT: bcmcmd is always run with stdin closed (</dev/null). Without it
# bcmcmd inherits the caller's stdin and eats the rest of the pair list that the
# while-loops read, so only the first VLAN got configured (fixed in V0.3.1).
#
# _tl_bcm <unit> <bcm shell command> -- streams the DUT output, returns bcmcmd rc
# Debug mode prints the full command line first; dry-run prints and skips it.
_tl_bcm() {
local unit="$1"; shift
local cmd="$*"
_tl_dbg "$BCMCMD_CMD -n $unit -c '$cmd'"
if [ "$TL_DRY_RUN" = "1" ]; then
echo "[DRY ] $BCMCMD_CMD -n $unit -c '$cmd'"
return 0
fi
"$BCMCMD_CMD" -n "$unit" -c "$cmd" </dev/null
}
# bcmcmd exits 0 even when the BCM shell itself refused the command
# ("VLAN: ERROR: Entry exists", "Failed to execute the diagnostic command"),
# so the captured output has to be pattern-matched as well.
TL_ERR_PAT="${TL_ERR_PAT:-ERROR|Failed to execute|Invalid|Syntax error|Unavailable|not found}"
TL_BENIGN_CREATE="${TL_BENIGN_CREATE:-Entry exists|already exist}"
TL_BENIGN_REMOVE="${TL_BENIGN_REMOVE:-Entry not found|not found|does not exist|no such}"
# _tl_bcm_run <unit> <benign_regex|""> <cmd...>
# 0 = ok, 2 = failed but benign (matches benign_regex), 1 = real failure
# Output is captured (shown in debug mode, or on a real failure) so the ~110
# vlan create/add lines do not flood the console.
_tl_bcm_run() {
local unit="$1" benign="$2"; shift 2
local cmd="$*" out rc
if [ "$TL_DRY_RUN" = "1" ]; then _tl_bcm "$unit" "$cmd" >/dev/null; return 0; fi
_tl_dbg "$BCMCMD_CMD -n $unit -c '$cmd'"
out=$("$BCMCMD_CMD" -n "$unit" -c "$cmd" </dev/null 2>&1); rc=$?
[ "$DEBUG_MODE" = "1" ] && [ -n "$out" ] && echo "$out"
if [ "$rc" -ne 0 ] || echo "$out" | grep -qiE "$TL_ERR_PAT"; then
if [ -n "$benign" ] && echo "$out" | grep -qiE "$benign"; then
_tl_dbg "benign: unit $unit '$cmd'"
return 2
fi
_tl_err "unit $unit: '$cmd' rc=$rc"
[ "$DEBUG_MODE" != "1" ] && [ -n "$out" ] && echo "$out" >&2
return 1
fi
return 0
}
# _tl_bcm_q <unit> <cmd> -- _tl_bcm_run with no benign pattern
_tl_bcm_q() { local unit="$1"; shift; _tl_bcm_run "$unit" "" "$@"; }
_tl_units_check() {
local u
for u in $TL_UNITS; do
case "$u" in
0|1) ;;
*) _tl_err "invalid unit '$u' (expect 0 or 1)"; return 1 ;;
esac
done
if [ "$TL_DRY_RUN" != "1" ] && ! command -v "$BCMCMD_CMD" >/dev/null 2>&1; then
_tl_err "$BCMCMD_CMD not found in PATH (run on the SONiC DUT, or set BCMCMD_CMD)"
return 1
fi
return 0
}
_tl_pair_count() { echo "$TL_PAIRS" | grep -c . ; }
# === Step 1: VLAN loopback setup (traffic_loopback_vlan_setting.ttl) =========
traffic_linespeed_init() {
_tl_units_check || return 1
local unit vid pa pb n fail exist rc
for unit in $TL_UNITS; do
n=0; fail=0; exist=0
_tl_info "unit $unit: VLAN loopback setup ($(_tl_pair_count) pairs, VLAN 30..137)"
_tl_bcm_q "$unit" "vlan remove $TL_DEFAULT_VLAN pbm=$TL_DEFAULT_VLAN_PBM" \
|| _tl_warn "unit $unit: clearing default VLAN $TL_DEFAULT_VLAN membership failed (continuing)"
# read on fd 3: bcmcmd must not be able to consume the pair list
while read -r vid pa pb <&3; do
[ -z "$vid" ] && continue
_tl_bcm_run "$unit" "$TL_BENIGN_CREATE" "vlan create $vid"; rc=$?
[ "$rc" = "1" ] && fail=$((fail+1))
[ "$rc" = "2" ] && exist=$((exist+1))
_tl_bcm_q "$unit" "vlan add $vid pbm=$pa,$pb" || fail=$((fail+1))
n=$((n+1))
done 3<<< "$TL_PAIRS"
if [ "$fail" -eq 0 ]; then
_tl_ok "unit $unit: $n VLANs configured$([ "$exist" -gt 0 ] && echo " ($exist already existed)"), port pairs added"
else
_tl_warn "unit $unit: $n VLANs processed, $fail command(s) failed$([ "$exist" -gt 0 ] && echo ", $exist already existed")"
fi
done
}
# === Step 2: clear counters ==================================================
traffic_linespeed_clear() {
_tl_units_check || return 1
local unit
for unit in $TL_UNITS; do
_tl_info "unit $unit: clear c"
_tl_bcm "$unit" "clear c" || { _tl_err "unit $unit: clear c failed"; return 1; }
_tl_ok "unit $unit: counters cleared"
done
}
# === Step 3 / 6: show counters ==============================================
traffic_linespeed_show() {
_tl_units_check || return 1
local unit
for unit in $TL_UNITS; do
echo "----- unit $unit : show c -----"
_tl_bcm "$unit" "show c" || { _tl_err "unit $unit: show c failed"; return 1; }
done
}
# === Step 4: start traffic (traffic_loopback_start.ttl) =====================
traffic_linespeed_start() {
_tl_units_check || return 1
local unit vid pa pb n fail
for unit in $TL_UNITS; do
n=0; fail=0
_tl_info "unit $unit: tx $TL_TX_COUNT length=$TL_TX_LENGTH per VLAN 30..137"
while read -r vid pa pb <&3; do
[ -z "$vid" ] && continue
_tl_bcm_q "$unit" "tx $TL_TX_COUNT length=$TL_TX_LENGTH VLantag=$vid" || fail=$((fail+1))
n=$((n+1))
done 3<<< "$TL_PAIRS"
if [ "$fail" -eq 0 ]; then
_tl_ok "unit $unit: $n bursts sent ($((n * TL_TX_COUNT)) packets, ${TL_TX_LENGTH}B each)"
else
_tl_warn "unit $unit: $n bursts attempted, $fail failed"
fi
done
}
# === Step 5: stop traffic (traffic_loopback_vlan_remove.ttl) ================
traffic_linespeed_stop() {
_tl_units_check || return 1
local unit vid pa pb n fail
for unit in $TL_UNITS; do
n=0; fail=0
_tl_info "unit $unit: removing loopback port pairs from VLAN 30..137"
while read -r vid pa pb <&3; do
[ -z "$vid" ] && continue
_tl_bcm_run "$unit" "$TL_BENIGN_REMOVE" "vlan remove $vid pbm=$pa,$pb"
[ "$?" = "1" ] && fail=$((fail+1))
if [ "$TL_DESTROY" = "1" ]; then
_tl_bcm_run "$unit" "$TL_BENIGN_REMOVE" "vlan destroy $vid"
[ "$?" = "1" ] && fail=$((fail+1))
fi
n=$((n+1))
done 3<<< "$TL_PAIRS"
if [ "$fail" -eq 0 ]; then
_tl_ok "unit $unit: $n VLANs cleaned$([ "$TL_DESTROY" = "1" ] && echo " and destroyed")"
else
_tl_warn "unit $unit: $n VLANs processed, $fail command(s) failed"
fi
done
}
# === Port status: ps <portlist> =============================================
# 'ps' shows link / speed / duplex / flow-control per port. Default list is the
# cabled ports ($TL_PS_PORTS); 'all' (or empty) runs a bare 'ps' = every port.
traffic_linespeed_ps() {
_tl_units_check || return 1
local unit cmd="ps"
case "${TL_PS_PORTS:-all}" in
all|ALL|"") cmd="ps" ;;
*) cmd="ps $TL_PS_PORTS" ;;
esac
for unit in $TL_UNITS; do
echo "----- unit $unit : $cmd -----"
_tl_bcm "$unit" "$cmd" || { _tl_err "unit $unit: $cmd failed"; return 1; }
done
}
# === Report: show c -> per-pair TX/RX table + PASS/FAIL =====================
# Mirrors tools/bcm_mibpair_report_V1.1.0.py, in awk so it runs on the DUT.
#
# Parses MIB_TPOK.cd1(2) : 723,629,081,133 +723,629,081,133
# MIB_RPOK.cd1(2) : 581,693,309,458 +581,693,309,458
# Verdict per pair: cdA.TPOK == cdB.RPOK and cdA.RPOK == cdB.TPOK
# PASS within tolerance / FAIL outside / NA when counters are missing
# (a cd port absent from 'show c' means the counter is zero or the
# console scrolled -- cross-check with 'ps' before blaming the port).
# _tl_pair_report <logfile> -> 0 all PASS, 2 any FAIL/NA, 1 parse error
_tl_pair_report() {
local log="$1" color=0
# PASS green / FAIL red / NA yellow. auto = only when stdout is a terminal,
# so redirecting to a file or piping into grep/diff stays plain text.
case "$TL_COLOR" in
always|1|yes) color=1 ;;
never|0|no) color=0 ;;
*) [ -t 1 ] && color=1 ;;
esac
[ "$TL_REPORT_TSV" = "1" ] && color=0
awk -v PAIRS="$TL_PAIRS" \
-v TOL="$TL_TOLERANCE" \
-v ALL="$TL_REPORT_ALL" \
-v GROUPED="$TL_REPORT_GROUPED" \
-v WITHPORT="$TL_REPORT_WITHPORT" \
-v QUIET="$TL_REPORT_QUIET" \
-v TSV="$TL_REPORT_TSV" \
-v COLOR="$color" '
function grp(v, s,out,l,i) {
s = sprintf("%d", v)
if (!GROUPED) return s
out = ""; l = length(s)
for (i = 1; i <= l; i++) {
out = out substr(s, i, 1)
if (((l - i) % 3) == 0 && i < l) out = out ","
}
return out
}
function pname(cd) { return (WITHPORT && (cd in LP)) ? cd "(" LP[cd] ")" : cd }
function absd(x) { return (x < 0) ? -x : x }
# colour the verdict token: PASS green, FAIL red, NA yellow
function vc(v) {
if (!COLOR) return v
if (v == "PASS") return "\033[32m" v "\033[0m"
if (v == "FAIL") return "\033[31m" v "\033[0m"
return "\033[33m" v "\033[0m"
}
function redl(s) { return COLOR ? "\033[31m" s "\033[0m" : s }
BEGIN {
NP = split(PAIRS, L, "\n")
for (i = 1; i <= NP; i++) { split(L[i], f, " "); VID[i] = f[1]; A[i] = f[2]; B[i] = f[3] }
MISSING = "-"
}
{
idx = index($0, "MIB_")
if (idx == 0) next
s = substr($0, idx)
kind = substr(s, 5, 4)
if (kind != "TPOK" && kind != "RPOK") next
p1 = index(s, "."); p2 = index(s, "("); p3 = index(s, ")"); p4 = index(s, ":")
if (p1 == 0 || p2 == 0 || p3 == 0 || p4 == 0 || p2 < p1 || p4 < p3) next
cd = substr(s, p1 + 1, p2 - p1 - 1)
if (cd !~ /^cd[0-9]+$/) next
lp = substr(s, p2 + 1, p3 - p2 - 1)
val = substr(s, p4 + 1)
sub(/^[ \t]+/, "", val)
if (val !~ /^[0-9,]/) next
sub(/[ \t].*$/, "", val)
gsub(/,/, "", val)
LP[cd] = lp
if (kind == "TPOK") { TX[cd] = val + 0; HTX[cd] = 1 } else { RX[cd] = val + 0; HRX[cd] = 1 }
seen++
}
END {
if (seen == 0) {
print "error: no MIB_TPOK/MIB_RPOK counter found in the log" > "/dev/stderr"
exit 1
}
npass = nfail = nna = 0; nrow = 0; nrep = 0; miss = ""; nf = 0
for (i = 1; i <= NP; i++) {
a = A[i]; b = B[i]
oka = (a in HTX) && (a in HRX); okb = (b in HTX) && (b in HRX)
if (!(oka && okb)) {
v = "NA"; nna++
miss = miss (miss == "" ? "" : " ") a "/" b
if (!ALL) continue
ta = oka ? grp(TX[a]) : MISSING; ra = oka ? grp(RX[a]) : MISSING
tb = okb ? grp(TX[b]) : MISSING; rb = okb ? grp(RX[b]) : MISSING
} else {
dtx = TX[a] - RX[b]; drx = RX[a] - TX[b]
d = absd(dtx); if (absd(drx) > d) d = absd(drx)
if (d <= TOL + 0) { v = "PASS"; npass++ }
else {
v = "FAIL"; nfail++
FL[++nf] = sprintf("# FAIL %s/%s: %s.TX-%s.RX=%+d %s.RX-%s.TX=%+d", \
a, b, a, b, dtx, a, b, drx)
}
ta = grp(TX[a]); ra = grp(RX[a]); tb = grp(TX[b]); rb = grp(RX[b])
}
nrep++
nrow++; C0[nrow] = pname(a); C1[nrow] = ta; C2[nrow] = ra; C3[nrow] = v
nrow++; C0[nrow] = pname(b); C1[nrow] = tb; C2[nrow] = rb; C3[nrow] = v
}
H0 = "Port"; H1 = "TX"; H2 = "RX"; H3 = "PASS/FAIL"
if (TSV) {
print H0 "\t" H1 "\t" H2 "\t" H3
for (r = 1; r <= nrow; r++) print C0[r] "\t" C1[r] "\t" C2[r] "\t" C3[r]
} else {
w0 = length(H0); w1 = length(H1); w2 = length(H2); w3 = length(H3)
for (r = 1; r <= nrow; r++) {
if (length(C0[r]) > w0) w0 = length(C0[r])
if (length(C1[r]) > w1) w1 = length(C1[r])
if (length(C2[r]) > w2) w2 = length(C2[r])
if (length(C3[r]) > w3) w3 = length(C3[r])
}
w0 += 2; w1 += 2; w2 += 2; w3 += 2
printf "%-*s%*s%*s%*s\n", w0, H0, w1, H1, w2, H2, w3, H3
for (r = 1; r <= nrow; r++) {
# pad on the plain text, then colour, so the columns stay aligned
printf "%-*s%*s%*s", w0, C0[r], w1, C1[r], w2, C2[r]
printf "%*s%s\n", w3 - length(C3[r]), "", vc(C3[r])
}
}
if (!QUIET) {
print ""
printf "# pairs in table : %d\n", NP
printf "# pairs reported : %d\n", nrep
printf "# %s %d %s %d %s %d (tolerance %d)\n", \
vc("PASS"), npass, vc("FAIL"), nfail, vc("NA"), nna, TOL + 0
if (miss != "") printf "# %s (no counters): %s\n", vc("NA"), miss
for (i = 1; i <= nf; i++) print redl(FL[i])
}
exit (nfail || nna) ? 2 : 0
}' "$log"
}
# traffic_linespeed_report -- 'show c' per unit, keep the raw log, render the table
traffic_linespeed_report() {
local unit log rc worst=0 stamp
# -f <log>: offline mode, no DUT access, no unit loop
if [ -n "$TL_REPORT_FILE" ]; then
[ -r "$TL_REPORT_FILE" ] || { _tl_err "cannot read log '$TL_REPORT_FILE'"; return 1; }
_tl_info "report from log: $TL_REPORT_FILE (tolerance $TL_TOLERANCE)"
_tl_pair_report "$TL_REPORT_FILE"; return $?
fi
_tl_units_check || return 1
if [ "$TL_DRY_RUN" = "1" ]; then
for unit in $TL_UNITS; do _tl_bcm "$unit" "show c" >/dev/null; done
return 0
fi
stamp=$(date +%Y%m%d_%H%M%S 2>/dev/null || echo now)
for unit in $TL_UNITS; do
log="$TL_LOG_DIR/showc_unit${unit}_${stamp}.log"
if ! _tl_bcm "$unit" "show c" > "$log" 2>&1; then
_tl_err "unit $unit: show c failed (see $log)"; return 1
fi
[ "$TL_REPORT_RAW" = "1" ] && { echo "----- unit $unit : raw show c -----"; cat "$log"; }
echo "===== unit $unit : loopback pair counter report (raw: $log) ====="
_tl_pair_report "$log"; rc=$?
[ "$rc" -gt "$worst" ] && worst=$rc
case "$rc" in
0) _tl_ok "unit $unit: all pairs PASS" ;;
2) _tl_warn "unit $unit: at least one pair FAIL/NA" ;;
*) _tl_err "unit $unit: report parse error" ;;
esac
done
return "$worst"
}
# === Convenience: full sequence ps -> 1 -> 2 -> 4 -> report ==================
traffic_linespeed_run() {
traffic_linespeed_ps || return 1
traffic_linespeed_init || return 1
traffic_linespeed_clear || return 1
traffic_linespeed_start || return 1
traffic_linespeed_report
local rc=$?
_tl_info "traffic done. 'stop' removes the loopback VLAN membership."
return $rc
}
# === Help ====================================================================
blanton_traffic_linespeed_help() {
echo -e "\033[1mblanton_traffic_linespeed.sh - SWB loopback line-speed traffic (bcmcmd)\033[0m"
echo ""
echo -e " Units: \033[36m$TL_UNITS\033[0m tx: \033[36m$TL_TX_COUNT pkt x ${TL_TX_LENGTH}B\033[0m"
echo -e " VLANs: 30..137 ($(_tl_pair_count) loopback pairs, cdN <-> cdN+32)"
echo ""
echo -e "\033[1mSubcommands\033[0m"
echo -e " \033[33minit\033[0m step 1 vlan remove 1 pbm=cd, vlan create/add each pair"
echo -e " \033[33mclear\033[0m step 2 clear c"
echo -e " \033[33mshow\033[0m step 3/6 show c (run before and after traffic)"
echo -e " \033[33mstart\033[0m step 4 tx <count> length=<len> VLantag=<vid> per VLAN"
echo -e " \033[33mstop\033[0m step 5 vlan remove <vid> pbm=<pair> [--destroy also destroys]"
echo -e " \033[33mps\033[0m ps <portlist> port status (link/speed) of the cabled ports"
echo -e " \033[33mreport\033[0m show c -> per-pair TX/RX table + PASS/FAIL cross-check"
echo -e " \033[33mrun\033[0m ps -> init -> clear -> start -> report"
echo -e " \033[33mhelp\033[0m"
echo ""
echo -e "\033[1mOptions\033[0m"
echo -e " \033[33m-u|--unit\033[0m <0|1|all> bcmcmd unit(s); 'all' = 0 1 (default: $TL_UNITS)"
echo -e " \033[33m-c|--count\033[0m <n> tx packet count (default $TL_TX_COUNT)"
echo -e " \033[33m-l|--length\033[0m <n> tx packet length (default $TL_TX_LENGTH)"
echo -e " \033[33m-p|--ports\033[0m <list> ps port list, or 'all' for every port"
echo -e " \033[33m-T|--tolerance\033[0m <n> report: allowed |delta| pkts (default $TL_TOLERANCE; -T 1 = snapshot skew)"
echo -e " \033[33m-a|--all\033[0m report: also list pairs with no counters"
echo -e " \033[33m-g|--grouped\033[0m report: comma-group values"
echo -e " \033[33m-P|--with-port\033[0m report: render cd1(2) instead of cd1"
echo -e " \033[33m-q|--quiet\033[0m report: no trailing summary"
echo -e " \033[33m--tsv\033[0m report: tab-separated (Excel)"
echo -e " \033[33m--raw\033[0m report: also dump the raw 'show c'"
echo -e " \033[33m--color|--no-color\033[0m report: force/disable \033[32mPASS\033[0m / \033[31mFAIL\033[0m / \033[33mNA\033[0m colouring"
echo -e " (default: colour only when stdout is a terminal)"
echo -e " \033[33m-f|--file\033[0m <log> report: parse a saved log, no DUT access"
echo -e " \033[33m-d|--debug\033[0m print every bcmcmd command line (stderr) and run it"
echo -e " \033[33m-n|--dry-run\033[0m print commands only, do not touch the DUT"
echo -e " \033[33m--destroy\033[0m stop: also 'vlan destroy' each VLAN"
echo ""
echo -e "\033[1mExamples\033[0m"
echo -e " blanton_traffic_linespeed ps -u 0"
echo -e " blanton_traffic_linespeed init -u 1"
echo -e " blanton_traffic_linespeed clear && blanton_traffic_linespeed start"
echo -e " blanton_traffic_linespeed report -u 1 -T 1"
echo -e " blanton_traffic_linespeed report -u 1 -q | grep -v PASS"
echo -e " blanton_traffic_linespeed report -f /tmp/showc_unit1_*.log -a"
echo -e " blanton_traffic_linespeed show -u 1 | grep -iE 'error|discard|drop'"
echo -e " blanton_traffic_linespeed start -c 1000 -l 9216 -d"
echo -e " blanton_traffic_linespeed stop --destroy"
echo ""
echo -e " report exit code: 0 = all pairs PASS, 2 = any FAIL/NA, 1 = parse error"
echo -e " Off-box equivalent: tools/bcm_mibpair_report_V1.1.0.py (python3)"
echo -e " ps ports: \033[36m$TL_PS_PORTS\033[0m"
echo -e " Table mirrors utils/traffic_loopback_vlan_{setting,remove}.ttl and"
echo -e " utils/traffic_loopback_start.ttl - keep them in sync."
}
# === Dispatcher ==============================================================
blanton_traffic_linespeed() {
local sub="${1:-help}"; shift 2>/dev/null || true
while [ $# -gt 0 ]; do
case "$1" in
-u|--unit)
case "$2" in
all|both|*,*) TL_UNITS=$(echo "$2" | sed 's/all\|both/0 1/; s/,/ /g') ;;
*) TL_UNITS="$2" ;;
esac
shift 2 ;;
-c|--count) TL_TX_COUNT="$2"; shift 2 ;;
-l|--length) TL_TX_LENGTH="$2"; shift 2 ;;
-p|--ports) TL_PS_PORTS="$2"; shift 2 ;;
-T|--tolerance) TL_TOLERANCE="$2"; shift 2 ;;
-f|--file) TL_REPORT_FILE="$2"; shift 2 ;;
-a|--all) TL_REPORT_ALL=1; shift ;;
-g|--grouped) TL_REPORT_GROUPED=1; shift ;;
-P|--with-port) TL_REPORT_WITHPORT=1; shift ;;
-q|--quiet) TL_REPORT_QUIET=1; shift ;;
--tsv) TL_REPORT_TSV=1; shift ;;
--raw) TL_REPORT_RAW=1; shift ;;
--color) TL_COLOR=always; shift ;;
--no-color) TL_COLOR=never; shift ;;
-d|--debug) DEBUG_MODE=1; shift ;;
-n|--dry-run) TL_DRY_RUN=1; shift ;;
--destroy) TL_DESTROY=1; shift ;;
-h|--help) blanton_traffic_linespeed_help; return 0 ;;
-*) _tl_err "unknown option '$1'"; return 1 ;;
*) # bare argument: only meaningful as the 'ps' port list
if [ "$sub" = "ps" ] || [ "$sub" = "status" ]; then
TL_PS_PORTS="$1"; shift
else
_tl_err "unexpected argument '$1'"; return 1
fi ;;
esac
done
case "$sub" in
init) traffic_linespeed_init ;;
clear) traffic_linespeed_clear ;;
show) traffic_linespeed_show ;;
start) traffic_linespeed_start ;;
stop) traffic_linespeed_stop ;;
ps|status) traffic_linespeed_ps ;;
report|rpt) traffic_linespeed_report ;;
run|all) traffic_linespeed_run ;;
debug) traffic_debug on ;;
help|-h|--help) blanton_traffic_linespeed_help ;;
*) _tl_err "unknown subcommand '$sub'"; blanton_traffic_linespeed_help; return 1 ;;
esac
}
# Executed directly -> dispatch. Sourced -> just expose the functions + help.
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
blanton_traffic_linespeed "$@"
else
blanton_traffic_linespeed_help
fi