Compare commits
2
Commits
V1.1.8
..
b84188322d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b84188322d | ||
|
|
b4148c9571 |
@@ -1,7 +1,7 @@
|
||||
; =============================================================================
|
||||
; Script A for Blanton
|
||||
; Version : V1.1.8
|
||||
; Date : 2026-09-06
|
||||
; Version : V1.1.9
|
||||
; Date : 2026-09-07
|
||||
; Author : ETWen
|
||||
; =============================================================================
|
||||
; Version History:
|
||||
@@ -419,6 +419,71 @@
|
||||
; happened to have the binaries locally would have shipped
|
||||
; a 100 MB zip while a clone shipped 200 KB. The bundle is
|
||||
; now the same either way
|
||||
; V1.1.9 2026-09-07
|
||||
; Blanton_Script/bcm_dsc_scan.sh Created V1.2.0 - batch SerDes DSC dump
|
||||
; Runs bcmcmd -n <unit> -c "dsh -c 'phydiag <port> dsc'"
|
||||
; over the whole port list (1 x 100G + 445 x 200G ranges),
|
||||
; both units, and leaves the raw log in <script_dir>/log
|
||||
; before copying it to /host/hw-eval/jobs
|
||||
; -f names the log (%u -> u0/u1, %t -> timestamp), -F
|
||||
; overwrites, -u picks one unit, -n dry runs, -C skips the
|
||||
; copy. -f takes a filename only; the directory comes from
|
||||
; -o
|
||||
; bcmcmd exits 0 even when the diag shell rejects a
|
||||
; command, so success is judged by counting 'SERDES DISPLAY
|
||||
; DIAG DATA END' markers, not by the exit code - the same
|
||||
; trap blanton_traffic_linespeed.sh hit
|
||||
; Paths come from $0, not $HOME, because Script A runs it
|
||||
; after sudo -i and a literal ~ would depend on the
|
||||
; environment. Ends with '### DSC_SCAN_DONE rc=<n>' for a
|
||||
; TTL wait to key on
|
||||
; Non-tty output is one line per port instead of a progress
|
||||
; bar, so it does not flood the Tera Term log
|
||||
; !! phydiag dsc is read-only and does not change any
|
||||
; SerDes setting, but a full scan is ~10 min per run.
|
||||
; ScriptA and ScriptC each do one, so the test is ~20
|
||||
; min longer than before. Do not set DELAY to 0
|
||||
; Blanton_Script/bcm_dsc_parse.sh Created V1.0.0 - offline DSC log to CSV
|
||||
; Host side. -i <raw log> -o <prefix> produces
|
||||
; <prefix>_lane.csv (one row per lane: TXEQ / RXFFE / DFE /
|
||||
; EYE / SNR) and <prefix>_pm.csv (one row per pm_id:
|
||||
; temperature / PLL / lane count / SNR-EYE stats)
|
||||
; POSIX sh + awk, mawk compatible, no Python. Lane rows are
|
||||
; read by the order of their bracket groups rather than
|
||||
; fixed columns, so re-spacing by the SDK does not break
|
||||
; it; a row whose group count is not 9 is marked UNPARSED
|
||||
; on stderr instead of being parsed wrongly
|
||||
; SD / LCK sticky '*' flags are kept as separate columns
|
||||
; and not interpreted; a BER field of '!chk_en' means the
|
||||
; checker was off and is passed through unchanged
|
||||
; ScriptA Collect the SerDes DSC baseline before 'show uptime'
|
||||
; ./Blanton_Script/bcm_dsc_scan.sh -f
|
||||
; blanton_dsc_ScriptA.log -F
|
||||
; The name has no %u, so the script inserts _u0 / _u1
|
||||
; itself and unit 1 cannot overwrite unit 0 - that is where
|
||||
; the file names ScriptC copies come from
|
||||
; ScriptA / ScriptB / ScriptC Remove the commented-out 100G PRBS blocks
|
||||
; The port_prbs_monitor.sh calls had been commented out in
|
||||
; all three since bring-up, together with a second copy of
|
||||
; the 100G port status block in ScriptA and the stale
|
||||
; bmc_monitor_ddr.sh block in ScriptC
|
||||
; port_prbs_monitor.sh still ships and still runs by hand;
|
||||
; only the dead call sites are gone
|
||||
; ScriptC Collect the closing DSC dump and carry the four logs out
|
||||
; ./Blanton_Script/bcm_dsc_scan.sh -f
|
||||
; blanton_dsc_ScriptC.log -F, then cp of
|
||||
; blanton_dsc_Script{A,C}_u{0,1}.log into
|
||||
; /host/hw-eval/jobs/
|
||||
; The scan already copies its own output there; these four
|
||||
; are a second pass so a failed copy inside the scan still
|
||||
; leaves the data on the USB stick
|
||||
; Blanton_Script Both new scripts need the execute bit on the DUT
|
||||
; !! git records them 100644 like every other .sh here, and
|
||||
; ScriptA / ScriptC invoke them as
|
||||
; ./Blanton_Script/bcm_dsc_scan.sh, so they join
|
||||
; bmc_monitor.sh and friends in the chmod +x line after
|
||||
; deployment. Without it both scans fail with Permission
|
||||
; denied and the macro walks straight past it
|
||||
; =============================================================================
|
||||
include "config.ttl"
|
||||
|
||||
@@ -619,36 +684,6 @@ sendln "config save -y"
|
||||
wait prompt_sonic_root
|
||||
sendln "show interfaces status Ethernet513,Ethernet514"
|
||||
|
||||
;=============================================================================================================
|
||||
; ========== 100G PRBS ==========
|
||||
;wait prompt_sonic_root
|
||||
;sendln "~/Blanton_Script/port_prbs_monitor.sh start"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "sleep 10"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "~/Blanton_Script/port_prbs_monitor.sh status"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "~/Blanton_Script/port_prbs_monitor.sh stop"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "~/Blanton_Script/port_prbs_monitor.sh report"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "~/Blanton_Script/port_prbs_monitor.sh clear"
|
||||
|
||||
; ========== 100G Port Status ==========
|
||||
;wait prompt_sonic_root
|
||||
;sendln "sudo sfputil lpmode off Ethernet513"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "sudo sfputil lpmode off Ethernet514"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "config interface -n asic0 startup Ethernet513"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "config interface -n asic1 startup Ethernet514"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "config save -y"
|
||||
|
||||
;wait prompt_sonic_root
|
||||
;sendln "show interfaces status Ethernet513,Ethernet514"
|
||||
;=============================================================================================================
|
||||
|
||||
; ========== First Traffic Test ==========
|
||||
; Silent MAC
|
||||
@@ -712,6 +747,10 @@ else
|
||||
|
||||
endif
|
||||
|
||||
; ========== Collect bcm dsc data ==========
|
||||
wait prompt_sonic_root
|
||||
sendln "./Blanton_Script/bcm_dsc_scan.sh -f blanton_dsc_ScriptA.log -F"
|
||||
|
||||
; ========== Show Power on uptime ==========
|
||||
wait prompt_sonic_root
|
||||
sendln "show uptime"
|
||||
|
||||
@@ -123,12 +123,6 @@ sendln "~/Blanton_Script/nfc_polling.sh status"
|
||||
wait prompt_sonic_root
|
||||
sendln "show interfaces status Ethernet513,Ethernet514"
|
||||
|
||||
;=============================================================================================================
|
||||
; ========== 100G PRBS ==========
|
||||
;wait prompt_sonic_root
|
||||
;sendln "~/Blanton_Script/port_prbs_monitor.sh start"
|
||||
;=============================================================================================================
|
||||
|
||||
|
||||
; ========== Traffic START ==========
|
||||
; Silent MAC
|
||||
|
||||
@@ -33,23 +33,6 @@ sendln "~/Blanton_Script/mgmt_ping_monitor.sh stop"
|
||||
wait prompt_sonic_root
|
||||
sendln "~/Blanton_Script/nfc_polling.sh stop"
|
||||
|
||||
;=============================================================================================================
|
||||
; 100G PRBS STOP
|
||||
;wait prompt_sonic_root
|
||||
;sendln "~/Blanton_Script/port_prbs_monitor.sh stop"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "sudo sfputil lpmode off Ethernet513"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "sudo sfputil lpmode off Ethernet514"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "config interface -n asic0 startup Ethernet513"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "config interface -n asic1 startup Ethernet514"
|
||||
;=============================================================================================================
|
||||
|
||||
|
||||
|
||||
|
||||
; ========== TAKE DATA ==========
|
||||
include "utils/setup_pmon.ttl"
|
||||
include "utils/show_dmesg.ttl"
|
||||
@@ -66,15 +49,6 @@ include "utils/show_pcie_error_reg_SSD.ttl"
|
||||
wait prompt_sonic_root
|
||||
sendln "/usr/sbin/ras-mc-ctl --summary"
|
||||
|
||||
; ========== BMC DDR TAKE DATA ==========
|
||||
;wait prompt_sonic_root
|
||||
;sendln "./Blanton_Script/bmc_monitor_ddr.sh stop"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "./Blanton_Script/bmc_monitor.sh stop"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "cat ./Blanton_Script/log/bmc_poll.log"
|
||||
;wait prompt_sonic_root
|
||||
;sendln "cat ./Blanton_Script/log/bmc_ddr.log"
|
||||
|
||||
; ========== BMC Monitor TAKE DATA ==========
|
||||
wait prompt_sonic_root
|
||||
@@ -108,10 +82,6 @@ sendln "~/Blanton_Script/nfc_polling.sh report"
|
||||
; ========== 100G Port ==========
|
||||
wait prompt_sonic_root
|
||||
sendln "show interfaces status Ethernet513,Ethernet514"
|
||||
;=============================================================================================================
|
||||
;wait prompt_sonic_root
|
||||
;sendln "cat ~/Blanton_Script/log/port_prbs.log "
|
||||
;=============================================================================================================
|
||||
|
||||
; ========== CHECK Traffic counters ==========
|
||||
if SWB_UNIT0 = 1 && SWB_UNIT1 = 1 then
|
||||
@@ -180,6 +150,10 @@ else
|
||||
|
||||
endif
|
||||
|
||||
; ========== Collect bcm dsc data ==========
|
||||
wait prompt_sonic_root
|
||||
sendln "./Blanton_Script/bcm_dsc_scan.sh -f blanton_dsc_ScriptC.log -F"
|
||||
|
||||
|
||||
; ========== NVME Err Info ==========
|
||||
wait prompt_sonic_root
|
||||
@@ -225,7 +199,14 @@ wait prompt_sonic_root
|
||||
sendln "cp ./Blanton_Script/log/mgmt_ping.log /host/hw-eval/jobs/"
|
||||
wait prompt_sonic_root
|
||||
sendln "cp ./Blanton_Script/log/nfc_poll.log /host/hw-eval/jobs/"
|
||||
|
||||
wait prompt_sonic_root
|
||||
sendln "cp ./Blanton_Script/log/blanton_dsc_ScriptA_u0.log /host/hw-eval/jobs/"
|
||||
wait prompt_sonic_root
|
||||
sendln "cp ./Blanton_Script/log/blanton_dsc_ScriptA_u1.log /host/hw-eval/jobs/"
|
||||
wait prompt_sonic_root
|
||||
sendln "cp ./Blanton_Script/log/blanton_dsc_ScriptC_u0.log /host/hw-eval/jobs/"
|
||||
wait prompt_sonic_root
|
||||
sendln "cp ./Blanton_Script/log/blanton_dsc_ScriptC_u1.log /host/hw-eval/jobs/"
|
||||
|
||||
wait prompt_sonic_root
|
||||
getdate ts_date "%Y%m%d"
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
#!/bin/sh
|
||||
# =============================================================================
|
||||
# bcm_dsc_parse_V1.0.0.sh
|
||||
#
|
||||
# Author : ETWen
|
||||
# Date : 20260906
|
||||
#
|
||||
# Version History
|
||||
# V1.0.0 20260906 Initial Version
|
||||
#
|
||||
# Description:
|
||||
# Parse Broadcom diag shell "phydiag <pm_id> dsc" raw log into CSV.
|
||||
# 支援多個 pm_id 串接在同一份 log,以 "pm_id =" 當 record separator。
|
||||
#
|
||||
# Output:
|
||||
# <prefix>_lane.csv : 每個 lane 一列(TXEQ / RXFFE / DFE / EYE / SNR ...)
|
||||
# <prefix>_pm.csv : 每個 pm_id 一列(溫度 / PLL / lane 數 / SNR-EYE 統計)
|
||||
#
|
||||
# Usage:
|
||||
# ./bcm_dsc_parse_V1.0.0.sh -i dsc_raw.log [-o out_prefix]
|
||||
#
|
||||
# Design notes:
|
||||
# - 純 POSIX sh + awk(mawk 相容),不依賴 Python。
|
||||
# - Lane row 採「括號群組出現順序」抽取,而非固定欄位位置,對排版空白
|
||||
# 變動免疫。SDK 若增減欄位,群組數不等於 9,該列會被標記 UNPARSED
|
||||
# 並輸出到 stderr,不會靜默解析錯誤。
|
||||
# - 已知 header 名稱數與資料欄數不一致之處:
|
||||
# * (P RX, CDRxN, UC_CFG, UC_STS, RST, STP) 6 個名字 / 7 個值
|
||||
# (RST 實際是 "0,0" 兩欄)
|
||||
# * TXEQ 尾端多一個逗號
|
||||
# * AVG_TMON 顯示為 "(11) 66C" 兩個 token
|
||||
# - SD / LCK 的 "*" 為 sticky 旗標,本版拆成獨立欄位保留,不做判讀。
|
||||
# - BER 欄若為 "!chk_en" 表示 checker 未開啟,值無效,原樣保留。
|
||||
# =============================================================================
|
||||
|
||||
set -e
|
||||
|
||||
IN=""
|
||||
PREFIX="dsc"
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 -i <raw_log> [-o <out_prefix>]" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
while getopts "i:o:h" opt; do
|
||||
case "$opt" in
|
||||
i) IN="$OPTARG" ;;
|
||||
o) PREFIX="$OPTARG" ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$IN" ] || usage
|
||||
[ -f "$IN" ] || { echo "ERROR: input not found: $IN" >&2; exit 2; }
|
||||
|
||||
LANE_CSV="${PREFIX}_lane.csv"
|
||||
PM_CSV="${PREFIX}_pm.csv"
|
||||
|
||||
# 先去掉 CR(console / telnet log 常帶),再交給 awk
|
||||
tr -d '\r' < "$IN" | awk -v LANE_CSV="$LANE_CSV" -v PM_CSV="$PM_CSV" '
|
||||
function trim(s) { sub(/^[ \t]+/, "", s); sub(/[ \t]+$/, "", s); return s }
|
||||
|
||||
# 掃描一列,取出所有頂層括號群組到 G[],群組前的英數 prefix 到 P[],
|
||||
# 其餘純量文字留在 REST。回傳群組數。
|
||||
function split_groups(line, i, ch, d, buf, ng, rest, prefix) {
|
||||
split("", G, ":"); split("", P, ":")
|
||||
ng = 0; d = 0; buf = ""; rest = ""; prefix = ""
|
||||
for (i = 1; i <= length(line); i++) {
|
||||
ch = substr(line, i, 1)
|
||||
if (ch == "(") {
|
||||
d++
|
||||
if (d == 1) {
|
||||
buf = ""; prefix = ""
|
||||
if (match(rest, /[A-Za-z_]+$/)) {
|
||||
prefix = substr(rest, RSTART, RLENGTH)
|
||||
rest = substr(rest, 1, RSTART - 1)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
if (ch == ")") {
|
||||
d--
|
||||
if (d == 0) { ng++; G[ng] = buf; P[ng] = prefix; rest = rest " "; continue }
|
||||
}
|
||||
if (d > 0) { buf = buf ch; continue }
|
||||
rest = rest ch
|
||||
}
|
||||
REST = rest
|
||||
return ng
|
||||
}
|
||||
|
||||
# 取括號群組的第 n 個子欄位(以逗號分隔)
|
||||
function gf(idx, n, a, c) {
|
||||
c = split(G[idx], a, ",")
|
||||
if (n > c) return ""
|
||||
return trim(a[n])
|
||||
}
|
||||
|
||||
function is_num(s) { return (s ~ /^[+-]?[0-9]+(\.[0-9]+)?$/) }
|
||||
|
||||
function reset_pm() {
|
||||
core = ""; sdtype = ""; hw_lanes = ""; lane_sel = ""
|
||||
live_temp = ""; avg_tmon = ""; pll_lock = ""
|
||||
n_lane = 0; snr_min = ""; snr_max = ""; eye_min = ""; all_lck = 1
|
||||
}
|
||||
|
||||
function flush_pm() {
|
||||
if (pm == "") return
|
||||
printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
|
||||
pm, core, sdtype, hw_lanes, lane_sel, n_lane,
|
||||
live_temp, avg_tmon, pll_lock,
|
||||
(snr_min == "" ? "NA" : snr_min),
|
||||
(eye_min == "" ? "NA" : eye_min),
|
||||
(n_lane == 0 ? "NA" : (all_lck ? "PASS" : "FAIL")) > PM_CSV
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
pm = ""; in_lane = 0; core_row = 0; bad = 0
|
||||
print "pm_id,core,lane,serdes_type,sd,sd_sticky,lck,lck_sticky,rxppm,txppm," \
|
||||
"txeq_n3,txeq_n2,txeq_n1,txeq_m,txeq_p1,txeq_p2,txeq_sum," \
|
||||
"rxffe_n3,rxffe_n2,rxffe_n1,rxffe_m,rxffe_p1,rxffe_p2," \
|
||||
"dfe1,dfe2,pf_m,pf_l,pf_h,vga,dco,tp0,tp1,tp2,flt_m,flt_s," \
|
||||
"nlc_u,nlc_l,eye_u,eye_m,eye_l,link_time,snr,ber," \
|
||||
"live_temp,avg_tmon,pll_lock,rx_pol,cdr,uc_cfg,uc_sts,rst,stp" > LANE_CSV
|
||||
print "pm_id,core,serdes_type,hw_lanes,lane_select,active_lanes," \
|
||||
"live_temp,avg_tmon,pll_lock,snr_min,eye_min,lck_all" > PM_CSV
|
||||
}
|
||||
|
||||
/^pm_id[ \t]*=/ { flush_pm(); pm = $NF; reset_pm(); in_lane = 0; next }
|
||||
/^Core[ \t]*=/ { if (match($0, /Core[ \t]*=[ \t]*[0-9]+/)) { core = $0; sub(/.*Core[ \t]*=[ \t]*/, "", core); sub(/[^0-9].*/, "", core) } next }
|
||||
/^SerDes type[ \t]*=/ { sdtype = $NF; next }
|
||||
/^Rev ID # Lanes/ { hw_lanes = $NF; next }
|
||||
/^lane_select[ \t]*=/ { lane_sel = $NF; next }
|
||||
|
||||
/^CORE[ \t]+RST_ST/ { core_row = 1; next }
|
||||
core_row == 1 {
|
||||
line = $0
|
||||
gsub(/\([^)]*\)[ \t]*/, "", line) # 去掉 AVG_TMON 的 "(11)"
|
||||
n = split(trim(line), c, /[ \t]+/)
|
||||
if (n >= 15) { live_temp = c[9]; avg_tmon = c[10]; pll_lock = c[15] }
|
||||
core_row = 0
|
||||
next
|
||||
}
|
||||
|
||||
/^LN[ \t]*\(/ { in_lane = 1; next }
|
||||
/SERDES DISPLAY DIAG DATA END/ { in_lane = 0; next }
|
||||
|
||||
in_lane && /^[0-9]+[ \t]*\(/ {
|
||||
ng = split_groups($0)
|
||||
if (ng != 9) {
|
||||
printf "UNPARSED (groups=%d) pm_id=%s: %s\n", ng, pm, $0 > "/dev/stderr"
|
||||
bad++
|
||||
next
|
||||
}
|
||||
nr = split(trim(REST), r, /[ \t]+/)
|
||||
if (nr != 10) {
|
||||
printf "UNPARSED (scalars=%d) pm_id=%s: %s\n", nr, pm, $0 > "/dev/stderr"
|
||||
bad++
|
||||
next
|
||||
}
|
||||
|
||||
lane = r[1]
|
||||
sd = r[2]; sd_st = (sd ~ /\*/) ? 1 : 0; gsub(/\*/, "", sd)
|
||||
lck = r[3]; lck_st = (lck ~ /\*/) ? 1 : 0; gsub(/\*/, "", lck)
|
||||
rxppm = r[4]; vga = r[5]; dco = r[6]; txppm = r[7]
|
||||
link_time = r[8]; snr = r[9]; ber = r[10]
|
||||
|
||||
# TXEQ 六個 tap 與總和(僅在全為數值時計算)
|
||||
tsum = 0; tok = 1
|
||||
for (i = 1; i <= 6; i++) {
|
||||
tq[i] = gf(7, i)
|
||||
if (is_num(tq[i])) tsum += tq[i]; else tok = 0
|
||||
}
|
||||
|
||||
printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,", pm, core, lane, sdtype, sd, sd_st, lck, lck_st, rxppm, txppm > LANE_CSV
|
||||
printf "%s,%s,%s,%s,%s,%s,%s,", tq[1], tq[2], tq[3], tq[4], tq[5], tq[6], (tok ? tsum : "NA") > LANE_CSV
|
||||
printf "%s,%s,%s,%s,%s,%s,", gf(4,1), gf(4,2), gf(4,3), gf(4,4), gf(4,5), gf(4,6) > LANE_CSV
|
||||
printf "%s,%s,", gf(5,1), gf(5,2) > LANE_CSV
|
||||
printf "%s,%s,%s,%s,%s,", gf(2,1), gf(2,2), gf(2,3), vga, dco > LANE_CSV
|
||||
printf "%s,%s,%s,%s,%s,", gf(3,1), gf(3,2), gf(3,3), gf(6,1), gf(6,2) > LANE_CSV
|
||||
printf "%s,%s,%s,%s,%s,", gf(8,1), gf(8,2), gf(9,1), gf(9,2), gf(9,3) > LANE_CSV
|
||||
printf "%s,%s,%s,", link_time, snr, ber > LANE_CSV
|
||||
printf "%s,%s,%s,", live_temp, avg_tmon, pll_lock > LANE_CSV
|
||||
printf "%s,%s,%s,%s,%s/%s,%s\n", gf(1,1), gf(1,2), gf(1,3), gf(1,4), gf(1,5), gf(1,6), gf(1,7) > LANE_CSV
|
||||
|
||||
# per-PM 統計
|
||||
n_lane++
|
||||
if (lck != "1" || sd != "1") all_lck = 0
|
||||
if (is_num(snr)) { if (snr_min == "" || snr + 0 < snr_min + 0) snr_min = snr }
|
||||
for (i = 1; i <= 3; i++) {
|
||||
e = gf(9, i)
|
||||
if (is_num(e)) { if (eye_min == "" || e + 0 < eye_min + 0) eye_min = e }
|
||||
}
|
||||
next
|
||||
}
|
||||
|
||||
END {
|
||||
flush_pm()
|
||||
if (bad > 0) printf "WARN: %d lane row(s) unparsed\n", bad > "/dev/stderr"
|
||||
}
|
||||
'
|
||||
|
||||
echo "OK: ${LANE_CSV}, ${PM_CSV}"
|
||||
@@ -0,0 +1,335 @@
|
||||
#!/bin/sh
|
||||
# =============================================================================
|
||||
# bcm_dsc_scan.sh
|
||||
#
|
||||
# Author : ETWen
|
||||
# Date : 20260906
|
||||
#
|
||||
# Version History
|
||||
# V1.0.0 20260906 Initial Version
|
||||
# V1.1.0 20260906 Log 預設輸出至 <script_dir>/log,掃描後自動複製到
|
||||
# /host/hw-eval/jobs/;非 tty 環境改用逐行進度輸出,
|
||||
# 避免灌爆 Tera Term logopen 的 log
|
||||
# V1.2.0 20260906 新增 -f 自訂 log 檔名(支援 %u / %t 佔位符)與
|
||||
# -F 強制覆蓋;預設不覆蓋既有檔案,自動加序號
|
||||
#
|
||||
# Description:
|
||||
# 在 Blanton SONiC DUT 上批次執行 Broadcom diag shell 的
|
||||
# "phydiag <port> dsc",收集 raw log 供離線解析。
|
||||
#
|
||||
# 實際下的指令為:
|
||||
# bcmcmd -n <unit> -c "dsh -c 'phydiag <port> dsc'"
|
||||
#
|
||||
# Deployment:
|
||||
# Script : /root/Blanton_Script/bcm_dsc_scan.sh
|
||||
# Log : /root/Blanton_Script/log/dsc_raw_u<unit>_<YYYYmmdd_HHMMSS>.log
|
||||
# Copy : /host/hw-eval/jobs/
|
||||
#
|
||||
# Log 檔名可用 -f 覆寫,支援兩個佔位符:
|
||||
# %u -> u0 / u1
|
||||
# %t -> YYYYmmdd_HHMMSS
|
||||
# -f 只接受檔名,不接受路徑;目錄一律由 -o 決定。
|
||||
# 同時掃兩個 unit 但檔名未含 %u 時,會自動在副檔名前插入 _u<unit>,
|
||||
# 避免第二個 unit 覆蓋掉第一個。
|
||||
#
|
||||
# 路徑由 $0 推導,不依賴 $HOME。Script A 以 sudo -i 執行時 ~ 為 /root,
|
||||
# 直接寫死 ~ 會有風險,故一律使用絕對路徑。
|
||||
#
|
||||
# Usage:
|
||||
# ./bcm_dsc_scan.sh # unit 0 與 1 都掃,掃完自動複製
|
||||
# ./bcm_dsc_scan.sh -u 0 # 只掃 unit 0
|
||||
# ./bcm_dsc_scan.sh -n # dry-run,只列出要掃的 port
|
||||
# ./bcm_dsc_scan.sh -C # 不複製到 /host/hw-eval/jobs
|
||||
# ./bcm_dsc_scan.sh -c /other/dir # 指定複製目的地
|
||||
# ./bcm_dsc_scan.sh -u 0 -f dsc_before_reboot.log
|
||||
# ./bcm_dsc_scan.sh -f blanton_dsc_%u_%t.log
|
||||
#
|
||||
# Exit code:
|
||||
# 0 = 全部 port 皆取得完整 dump
|
||||
# 1 = 有 port 未取得完整 dump,或複製失敗
|
||||
# 2 = 參數 / 路徑錯誤
|
||||
# 3 = 找不到 bcmcmd
|
||||
#
|
||||
# 結束時固定輸出一行 "### DSC_SCAN_DONE rc=<n>",供 TTL 端 wait 判斷。
|
||||
#
|
||||
# Design notes:
|
||||
# - 純 POSIX sh,不依賴 bash / Python。
|
||||
# - Port 清單以「範圍字串」定義在下方 PORTS_* 變數,展開後去重。
|
||||
# 100G 清單先展開,故重複的 port 會被判定為 100G(目前僅 port 268)。
|
||||
# - bcmcmd 即使指令失敗通常仍回傳 0,因此不以 exit code 判斷成功與否;
|
||||
# 改由 marker 行 + "SERDES DISPLAY DIAG DATA END" 數量比對。
|
||||
# - 先寫入本機 log 目錄再複製,而非直接寫遠端,避免中途失敗遺失整份資料。
|
||||
#
|
||||
# Assumptions:
|
||||
# - unit 0 與 unit 1 使用相同的 port 清單。
|
||||
# - phydiag 的參數為此清單中的 port 編號(非 port name)。
|
||||
# - 由 root 執行(Script A 的 sudo -i 之後),故不呼叫 sudo。
|
||||
#
|
||||
# Cautions:
|
||||
# - 全清單約 445 port x 2 unit,估計 10 分鐘,請勿把 DELAY 設為 0。
|
||||
# - phydiag ... dsc 為唯讀操作,不會變更 SerDes 設定。
|
||||
# =============================================================================
|
||||
|
||||
set -u
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Port list (以空白分隔的範圍或單一值)
|
||||
# ----------------------------------------------------------------------------
|
||||
PORTS_100G="268"
|
||||
PORTS_200G="1-24 40-96 112-168 184-240 256-308 310-327 342-380 382-399 414-452 454-471 486-524 526-543 558-565"
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Paths / defaults
|
||||
# ----------------------------------------------------------------------------
|
||||
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) || exit 2
|
||||
|
||||
UNITS="0 1"
|
||||
OUTDIR="${SCRIPT_DIR}/log"
|
||||
COPYDIR="/host/hw-eval/jobs"
|
||||
DELAY="0.2"
|
||||
DRYRUN=0
|
||||
DOCOPY=1
|
||||
LOGNAME="" # -f 指定的 log 檔名,空字串表示使用預設命名
|
||||
FORCE=0 # -F 允許覆蓋既有檔案
|
||||
PROGRESS_EVERY=50 # 非 tty 環境每 N 筆印一行進度
|
||||
|
||||
usage() {
|
||||
cat >&2 << 'USAGE'
|
||||
Usage: bcm_dsc_scan.sh [-u <0|1>] [-o <outdir>] [-f <logname>] [-F]
|
||||
[-c <copydir>] [-C] [-d <sec>] [-n] [-h]
|
||||
|
||||
-u <unit> 只掃指定 unit。省略則 unit 0 與 1 都掃。
|
||||
-o <dir> log 輸出目錄 (default: <script_dir>/log)
|
||||
-f <name> 自訂 log 檔名(僅檔名,不含路徑)
|
||||
佔位符: %u -> u0/u1 , %t -> YYYYmmdd_HHMMSS
|
||||
default: dsc_raw_%u_%t.log
|
||||
-F 允許覆蓋既有同名檔案 (default: 自動加序號 _1 / _2 ...)
|
||||
-c <dir> 掃描後複製目的地 (default: /host/hw-eval/jobs)
|
||||
-C 不複製
|
||||
-d <sec> 每筆 phydiag 之間的間隔 (default: 0.2)
|
||||
-n dry-run,列出展開後的 port 清單與指令,不實際執行
|
||||
-h 顯示此說明
|
||||
USAGE
|
||||
exit 1
|
||||
}
|
||||
|
||||
while getopts "u:o:f:Fc:Cd:nh" opt; do
|
||||
case "$opt" in
|
||||
u)
|
||||
case "$OPTARG" in
|
||||
0|1) UNITS="$OPTARG" ;;
|
||||
*) echo "ERROR: -u 只接受 0 或 1 (got: $OPTARG)" >&2; exit 2 ;;
|
||||
esac
|
||||
;;
|
||||
o) OUTDIR="$OPTARG" ;;
|
||||
f)
|
||||
case "$OPTARG" in
|
||||
"") echo "ERROR: -f 不可為空" >&2; exit 2 ;;
|
||||
*/*) echo "ERROR: -f 只接受檔名,路徑請用 -o (got: $OPTARG)" >&2; exit 2 ;;
|
||||
*) LOGNAME="$OPTARG" ;;
|
||||
esac
|
||||
;;
|
||||
F) FORCE=1 ;;
|
||||
c) COPYDIR="$OPTARG" ;;
|
||||
C) DOCOPY=0 ;;
|
||||
d) DELAY="$OPTARG" ;;
|
||||
n) DRYRUN=1 ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
mkdir -p "$OUTDIR" 2>/dev/null || { echo "ERROR: cannot create outdir: $OUTDIR" >&2; exit 2; }
|
||||
[ -w "$OUTDIR" ] || { echo "ERROR: outdir not writable: $OUTDIR" >&2; exit 2; }
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# sleep 是否支援小數秒(busybox sleep 可能只吃整數)
|
||||
# ----------------------------------------------------------------------------
|
||||
if ! sleep "$DELAY" 2>/dev/null; then
|
||||
echo "WARN: sleep '$DELAY' 不支援,改用 sleep 1" >&2
|
||||
DELAY="1"
|
||||
fi
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# 展開 port 清單。100G 先展開,重複者以 100G 為準。
|
||||
# 輸出格式: "<port>,<speed>",依 port 數值排序。
|
||||
# ----------------------------------------------------------------------------
|
||||
PORTFILE=$(mktemp) || { echo "ERROR: mktemp failed" >&2; exit 2; }
|
||||
trap 'rm -f "$PORTFILE"' EXIT
|
||||
trap 'echo "" >&2; echo "### DSC_SCAN_DONE rc=130 (INTERRUPTED)" >&2; rm -f "$PORTFILE"; exit 130' INT TERM
|
||||
|
||||
awk -v a="$PORTS_100G" -v b="$PORTS_200G" '
|
||||
function expand(s, spd, n, i, j, p, k, lo, hi) {
|
||||
n = split(s, p, /[ \t]+/)
|
||||
for (i = 1; i <= n; i++) {
|
||||
if (p[i] == "") continue
|
||||
if (split(p[i], k, "-") == 2) { lo = k[1] + 0; hi = k[2] + 0 }
|
||||
else { lo = p[i] + 0; hi = lo }
|
||||
if (hi < lo) { printf "ERROR: bad range %s\n", p[i] > "/dev/stderr"; continue }
|
||||
for (j = lo; j <= hi; j++) if (!(j in SPD)) SPD[j] = spd
|
||||
}
|
||||
}
|
||||
BEGIN {
|
||||
expand(a, "100G") # 先展開 100G,重複的 port 由 100G 取得
|
||||
expand(b, "200G")
|
||||
for (p in SPD) print p "," SPD[p]
|
||||
}' | sort -t, -k1,1n > "$PORTFILE"
|
||||
|
||||
TOTAL=$(wc -l < "$PORTFILE" | tr -d ' ')
|
||||
[ "$TOTAL" -gt 0 ] || { echo "ERROR: port list is empty" >&2; exit 2; }
|
||||
|
||||
N100=$(grep -c ',100G$' "$PORTFILE" || true)
|
||||
N200=$(grep -c ',200G$' "$PORTFILE" || true)
|
||||
|
||||
echo "Script dir : $SCRIPT_DIR" >&2
|
||||
echo "Log dir : $OUTDIR" >&2
|
||||
echo "Port list : $TOTAL ports (200G x2: $N200, 100G: $N100)" >&2
|
||||
echo "Units : $UNITS" >&2
|
||||
echo "Delay : ${DELAY}s" >&2
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Dry-run
|
||||
# ----------------------------------------------------------------------------
|
||||
if [ "$DRYRUN" -eq 1 ]; then
|
||||
for U in $UNITS; do
|
||||
echo "--- unit $U ---"
|
||||
while IFS=, read -r P SPD; do
|
||||
printf 'bcmcmd -n %s -c "dsh -c '\''phydiag %s dsc'\''" # %s\n' "$U" "$P" "$SPD"
|
||||
done < "$PORTFILE"
|
||||
done
|
||||
echo "### DSC_SCAN_DONE rc=0 (dry-run)" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
command -v bcmcmd > /dev/null 2>&1 || {
|
||||
echo "ERROR: bcmcmd not found in PATH" >&2
|
||||
echo "### DSC_SCAN_DONE rc=3" >&2
|
||||
exit 3
|
||||
}
|
||||
|
||||
# stderr 是 tty 才用 \r 進度條;被導向檔案(Tera Term logopen)時改逐行輸出
|
||||
if [ -t 2 ]; then TTY=1; else TTY=0; fi
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Log 檔名處理
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# 純 POSIX 的字串取代,避免 sed 對檔名中的特殊字元誤判
|
||||
replace_all() { # $1=haystack $2=needle $3=replacement
|
||||
_s=$1; _n=$2; _r=$3; _out=""
|
||||
while :; do
|
||||
case "$_s" in
|
||||
*"$_n"*)
|
||||
_out="${_out}${_s%%"$_n"*}${_r}"
|
||||
_s=${_s#*"$_n"}
|
||||
;;
|
||||
*) _out="${_out}${_s}"; break ;;
|
||||
esac
|
||||
done
|
||||
printf '%s' "$_out"
|
||||
}
|
||||
|
||||
# 依 unit 組出 log 檔名。未指定 -f 時使用預設命名。
|
||||
build_logname() { # $1=unit
|
||||
_u=$1
|
||||
_n=${LOGNAME:-dsc_raw_%u_%t.log}
|
||||
case "$_n" in *%u*) _has_u=1 ;; *) _has_u=0 ;; esac
|
||||
_n=$(replace_all "$_n" '%u' "u${_u}")
|
||||
_n=$(replace_all "$_n" '%t' "$TS")
|
||||
# 掃兩個 unit 但檔名沒有 %u,第二個 unit 會覆蓋第一個,故自動加上 _u<unit>
|
||||
if [ "$_has_u" -eq 0 ] && [ "$MULTI" -eq 1 ]; then
|
||||
case "$_n" in
|
||||
*.*) _n="${_n%.*}_u${_u}.${_n##*.}" ;;
|
||||
*) _n="${_n}_u${_u}" ;;
|
||||
esac
|
||||
fi
|
||||
printf '%s' "$_n"
|
||||
}
|
||||
|
||||
# 檔案已存在時自動加序號,避免覆蓋掉先前的掃描結果(-F 可強制覆蓋)
|
||||
uniquify() { # $1=filename
|
||||
_f=$1
|
||||
[ -e "${OUTDIR}/${_f}" ] || { printf '%s' "$_f"; return; }
|
||||
if [ "$FORCE" -eq 1 ]; then
|
||||
echo "WARN: 覆蓋既有檔案 ${OUTDIR}/${_f}" >&2
|
||||
printf '%s' "$_f"; return
|
||||
fi
|
||||
case "$_f" in
|
||||
*.*) _b=${_f%.*}; _e=".${_f##*.}" ;;
|
||||
*) _b=$_f; _e="" ;;
|
||||
esac
|
||||
_i=1
|
||||
while [ -e "${OUTDIR}/${_b}_${_i}${_e}" ]; do _i=$((_i + 1)); done
|
||||
echo "WARN: ${_f} 已存在,改用 ${_b}_${_i}${_e}(-F 可強制覆蓋)" >&2
|
||||
printf '%s' "${_b}_${_i}${_e}"
|
||||
}
|
||||
|
||||
MULTI=0
|
||||
[ "$(echo "$UNITS" | wc -w | tr -d ' ')" -gt 1 ] && MULTI=1
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Scan
|
||||
# ----------------------------------------------------------------------------
|
||||
TS=$(date +%Y%m%d_%H%M%S)
|
||||
RC=0
|
||||
LOGS=""
|
||||
|
||||
for U in $UNITS; do
|
||||
LOG="${OUTDIR}/$(uniquify "$(build_logname "$U")")"
|
||||
: > "$LOG" || { echo "ERROR: cannot write $LOG" >&2; exit 2; }
|
||||
LOGS="$LOGS $LOG"
|
||||
|
||||
printf '### SCAN_BEGIN unit=%s time=%s total=%s script=bcm_dsc_scan_V1.1.0\n' \
|
||||
"$U" "$(date '+%Y-%m-%d %H:%M:%S')" "$TOTAL" >> "$LOG"
|
||||
|
||||
echo "[unit $U] scanning $TOTAL ports -> $LOG" >&2
|
||||
|
||||
SEQ=0
|
||||
while IFS=, read -r P SPD; do
|
||||
SEQ=$((SEQ + 1))
|
||||
printf '### PORT unit=%s port=%s speed=%s seq=%s/%s\n' "$U" "$P" "$SPD" "$SEQ" "$TOTAL" >> "$LOG"
|
||||
bcmcmd -n "$U" -c "dsh -c 'phydiag $P dsc'" >> "$LOG" 2>&1 || RC=1
|
||||
|
||||
if [ "$TTY" -eq 1 ]; then
|
||||
printf '\r[unit %s] %s/%s port=%-5s' "$U" "$SEQ" "$TOTAL" "$P" >&2
|
||||
elif [ $((SEQ % PROGRESS_EVERY)) -eq 0 ] || [ "$SEQ" -eq "$TOTAL" ]; then
|
||||
printf '[unit %s] %s/%s port=%s\n' "$U" "$SEQ" "$TOTAL" "$P" >&2
|
||||
fi
|
||||
|
||||
sleep "$DELAY"
|
||||
done < "$PORTFILE"
|
||||
|
||||
[ "$TTY" -eq 1 ] && printf '\n' >&2
|
||||
printf '### SCAN_END unit=%s time=%s\n' "$U" "$(date '+%Y-%m-%d %H:%M:%S')" >> "$LOG"
|
||||
|
||||
# 概略檢查:每筆 PORT marker 應對應一個 dsc 區塊
|
||||
MARK=$(grep -c '^### PORT ' "$LOG" || true)
|
||||
DUMP=$(grep -c 'SERDES DISPLAY DIAG DATA END' "$LOG" || true)
|
||||
if [ "$MARK" -ne "$DUMP" ]; then
|
||||
echo "WARN: unit $U markers=$MARK dsc_blocks=$DUMP (有 port 未取得完整 dump)" >&2
|
||||
RC=1
|
||||
fi
|
||||
echo "Saved: $LOG (${DUMP}/${MARK} dumps)" >&2
|
||||
done
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Copy to job directory
|
||||
# ----------------------------------------------------------------------------
|
||||
if [ "$DOCOPY" -eq 1 ]; then
|
||||
if mkdir -p "$COPYDIR" 2>/dev/null && [ -w "$COPYDIR" ]; then
|
||||
for L in $LOGS; do
|
||||
if cp -p "$L" "$COPYDIR/"; then
|
||||
echo "Copied: $COPYDIR/$(basename "$L")" >&2
|
||||
else
|
||||
echo "ERROR: copy failed: $L -> $COPYDIR" >&2
|
||||
RC=1
|
||||
fi
|
||||
done
|
||||
sync
|
||||
else
|
||||
echo "ERROR: copydir not writable: $COPYDIR (log 仍保留在 $OUTDIR)" >&2
|
||||
RC=1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "### DSC_SCAN_DONE rc=${RC}" >&2
|
||||
exit "$RC"
|
||||
Reference in New Issue
Block a user