driveAtlas: redraw the Sliger CX4712 as a real front elevation
Lint / Shell (shellcheck) (push) Successful in 10s
Lint / Shell (shellcheck) (push) Successful in 10s
The old 10-bay render was a 138-column strip of ten wide horizontal boxes,
which is not what this chassis looks like. Sliger's own photos and CAD show
the ten 3.5" bays are a SINGLE ROW of ten with the drives mounted ON EDGE,
so from the front each bay is a tall narrow slot - ten thin vertical bars in
a row is the signature of the case. Drawn as stacked horizontal trays it was
simply wrong.
Redraw it as a true front elevation at the real proportions (4U, 431.8 x
176.8 mm, ~2.75:1 with rack ears):
- two 5.25" filler blanks side by side directly above the bay row, spanning
the same width (these are unused on all three nodes)
- one row of ten vertical bay slots, solid when occupied, dim ghost when not
- the narrow front I/O strip at the right: power ring, 2x USB-A, USB-C, and
the drive-lock key cylinder
- rack ears with vertical handles bracketing both edges
There are no front fans, no reset button, no badge and no per-bay LEDs on
this chassis, so none are drawn.
All text is pushed out of the picture into BAY/DEV caption rows column-aligned
under the slots, keeping the chassis a clean silhouette. M.2 and internal
drives are not on the front face, so they are listed as a legend beneath.
Also surface the two facts that actually matter for using this map: the case
has NO backplane (each bay is an individually hand-cabled SFF-8482 to SATA
adapter) and NO per-bay LEDs, so bay numbering is a LOCAL left-to-right
convention rather than a vendor one, and a drive can only be identified
physically by its serial - there is no bay LED to blink.
Fixed 88-column canvas, identical display width on every line in monochrome
and colour, colour runs coalesced per run of like bays to keep escape churn
low, SAFE width-1 glyphs only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+128
-53
@@ -446,74 +446,149 @@ run_diagnose() {
|
|||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
# generate_10bay_layout
|
# generate_10bay_layout
|
||||||
#
|
#
|
||||||
# Generates ASCII art representation of a 10-bay hot-swap chassis (Sliger CX4712).
|
# FRONT ELEVATION of a Sliger CX4712 4U rackmount chassis (faceplate removed).
|
||||||
# Shows storage controllers, M.2 NVMe slot, and 10 front hot-swap bays.
|
# - 2x 5.25" filler blanks side by side, spanning the bay region
|
||||||
|
# - 10x 3.5" hot-swap bays in ONE row; drives sit on edge, so each bay reads
|
||||||
|
# as a tall narrow slot -- the signature of this chassis
|
||||||
|
# - narrow front I/O strip (power ring, 2x USB-A, USB-C, key lock)
|
||||||
|
# - rack ears with vertical handles on both sides
|
||||||
|
# Occupied bays are solid, empty bays are dim ghosts. M.2 / internal drives are
|
||||||
|
# not on the front face, so they are listed as a legend under the drawing.
|
||||||
|
#
|
||||||
|
# Chassis has NO backplane and NO per-bay LEDs: each bay is an individually
|
||||||
|
# hand-cabled SFF-8482 to SATA adapter, so bay numbering is a LOCAL
|
||||||
|
# left-to-right convention, not a vendor one, and a drive can only be
|
||||||
|
# identified physically by its SERIAL (there is no bay LED to blink).
|
||||||
#
|
#
|
||||||
# Args:
|
# Args:
|
||||||
# $1 - Hostname to display in the layout header
|
# $1 - Hostname to display in the header
|
||||||
#
|
#
|
||||||
# Side effects: Calls build_drive_map() to populate DRIVE_MAP
|
# Side effects: Calls build_drive_map() to populate DRIVE_MAP
|
||||||
|
#
|
||||||
|
# The art printfs embed our own ANSI colour vars (DA_*) and literal box-drawing
|
||||||
|
# in the format string by design; device names are passed as %s args.
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
# shellcheck disable=SC2059
|
||||||
generate_10bay_layout() {
|
generate_10bay_layout() {
|
||||||
local hostname="$1"
|
local host="${1:-unknown}"
|
||||||
build_drive_map
|
local i j k n st run cell c s lp
|
||||||
|
|
||||||
# Box interior width = 136 (determined by 10 bay boxes: 4 + 10*13 + 2)
|
if declare -F build_drive_map >/dev/null 2>&1; then
|
||||||
# Total box width = 138 (136 interior + 2 for │ borders)
|
build_drive_map 2>/dev/null || true
|
||||||
|
|
||||||
# Main chassis section
|
|
||||||
printf "┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐\n"
|
|
||||||
printf "│ %-132s │\n" "$hostname - Sliger CX4712 (10x 3.5\" Hot-swap)"
|
|
||||||
printf "│%-136s│\n" ""
|
|
||||||
|
|
||||||
# Show storage controllers
|
|
||||||
printf "│ %-134s│\n" "Storage Controllers:"
|
|
||||||
while IFS= read -r ctrl; do
|
|
||||||
[[ -n "$ctrl" ]] && printf "│ %-134.134s│\n" "$ctrl"
|
|
||||||
done < <(get_storage_controllers)
|
|
||||||
printf "│%-136s│\n" ""
|
|
||||||
|
|
||||||
# M.2 NVMe slot if present
|
|
||||||
if [[ -n "${DRIVE_MAP[m2-1]}" ]]; then
|
|
||||||
printf "│ %-134s│\n" " M.2 NVMe: ${DRIVE_MAP[m2-1]}"
|
|
||||||
printf "│%-136s│\n" ""
|
|
||||||
fi
|
fi
|
||||||
|
if ! declare -p DRIVE_MAP >/dev/null 2>&1; then
|
||||||
# Internal (non-bay) drives if present
|
declare -g -A DRIVE_MAP=()
|
||||||
local int_keys
|
|
||||||
int_keys="$(printf '%s\n' "${!DRIVE_MAP[@]}" | grep -E '^int-' | sort)"
|
|
||||||
if [[ -n "$int_keys" ]]; then
|
|
||||||
while IFS= read -r int_key; do
|
|
||||||
printf "│ %-134s│\n" " Internal (non-bay): ${DRIVE_MAP[$int_key]}"
|
|
||||||
done <<< "$int_keys"
|
|
||||||
printf "│%-136s│\n" ""
|
|
||||||
fi
|
fi
|
||||||
|
if declare -F da_color_init >/dev/null 2>&1; then da_color_init; fi
|
||||||
printf "│ %-134s│\n" " Front Hot-swap Bays:"
|
# make every palette global exist -- safe under `set -u`, no-op when set
|
||||||
printf "│%-136s│\n" ""
|
for s in DA_ST DA_HL DA_DIM DA_OK DA_YEL DA_HOT DA_BLU DA_CYN DA_RST DA_SR; do
|
||||||
|
printf -v "$s" '%s' "${!s-}"
|
||||||
# Bay top borders
|
|
||||||
printf "│ "
|
|
||||||
for bay in {1..10}; do
|
|
||||||
printf "┌──────────┐ "
|
|
||||||
done
|
done
|
||||||
printf " │\n"
|
|
||||||
|
|
||||||
# Bay contents
|
# ---- geometry -----------------------------------------------------------
|
||||||
printf "│ "
|
# display width 88 = |1| 1 ear 3 | 2 | bays 61 | 5 | I/O 8 | 2 | ear 3 | 1 |1|
|
||||||
for bay in {1..10}; do
|
# bay strip 61 = wall + 10 slots of 5 cols separated by 1 col + wall
|
||||||
printf "│%-2d:%-7s│ " "$bay" "${DRIVE_MAP[$bay]:-EMPTY}"
|
local -a state=() namec=() numc=()
|
||||||
|
for ((i = 1; i <= 10; i++)); do
|
||||||
|
s="${DRIVE_MAP[$i]:-}"
|
||||||
|
if [ -n "$s" ]; then state[i]=on; else state[i]=off; s='--'; fi
|
||||||
|
printf -v c '%-5.5s' "$s" # hard truncate: borders can never move
|
||||||
|
s="${c%% *}"
|
||||||
|
lp=$(( (5 - ${#s}) / 2 ))
|
||||||
|
printf -v c '%*s%s%*s' "$lp" '' "$s" "$((5 - lp - ${#s}))" ''
|
||||||
|
namec[i]="$c"
|
||||||
|
s="$i"
|
||||||
|
lp=$(( (5 - ${#s}) / 2 ))
|
||||||
|
printf -v c '%*s%s%*s' "$lp" '' "$s" "$((5 - lp - ${#s}))" ''
|
||||||
|
numc[i]="$c"
|
||||||
done
|
done
|
||||||
printf " │\n"
|
|
||||||
|
|
||||||
# Bay bottom borders
|
# ---- coalesced colour runs: one escape pair per RUN of like bays --------
|
||||||
printf "│ "
|
local fillrow='' namerow='' numrow=''
|
||||||
for bay in {1..10}; do
|
i=1
|
||||||
printf "└──────────┘ "
|
while [ "$i" -le 10 ]; do
|
||||||
|
st="${state[i]}"
|
||||||
|
j="$i"
|
||||||
|
while [ "$j" -le 10 ] && [ "${state[j]}" = "$st" ]; do j=$((j + 1)); done
|
||||||
|
n=$((j - i))
|
||||||
|
if [ "$st" = on ]; then cell='█████'; c="$DA_OK"; else cell='░░░░░'; c="$DA_DIM"; fi
|
||||||
|
run=''
|
||||||
|
for ((k = 0; k < n; k++)); do [ "$k" -gt 0 ] && run+=' '; run+="$cell"; done
|
||||||
|
fillrow+="${c}${run}${DA_SR}"
|
||||||
|
run=''
|
||||||
|
for ((k = i; k < j; k++)); do [ "$k" -gt "$i" ] && run+=' '; run+="${namec[k]}"; done
|
||||||
|
namerow+="${c}${run}${DA_SR}"
|
||||||
|
if [ "$j" -le 10 ]; then fillrow+=' '; namerow+=' '; fi
|
||||||
|
i="$j"
|
||||||
|
done
|
||||||
|
for ((i = 1; i <= 10; i++)); do
|
||||||
|
[ "$i" -gt 1 ] && numrow+=' '
|
||||||
|
numrow+="${numc[i]}"
|
||||||
done
|
done
|
||||||
printf " │\n"
|
|
||||||
|
|
||||||
printf "└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘\n"
|
# ---- header -------------------------------------------------------------
|
||||||
|
local hostp tail
|
||||||
|
printf -v hostp '%-24.24s' "$host"
|
||||||
|
printf -v tail '%-52.52s' 'Sliger CX4712 - 4U - 431.8 x 176.8 mm'
|
||||||
|
printf '\n'
|
||||||
|
printf "${DA_CYN}driveAtlas${DA_SR} ${DA_HL}%s${DA_SR} ${DA_ST}%s${DA_RST}\n" "$hostp" "$tail"
|
||||||
|
printf -v tail '%-88.88s' 'FRONT ELEVATION - mesh faceplate removed - drives mounted on edge'
|
||||||
|
printf "${DA_DIM}%s${DA_RST}\n" "$tail"
|
||||||
|
printf '\n'
|
||||||
|
|
||||||
|
# ---- chassis ------------------------------------------------------------
|
||||||
|
local T525='┌─────────────────────────────┬─────────────────────────────┐'
|
||||||
|
local M525='│ 5.25" FILLER │ 5.25" FILLER │'
|
||||||
|
local B525='└─────────────────────────────┴─────────────────────────────┘'
|
||||||
|
local TBAY='┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐'
|
||||||
|
local BBAY='└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘'
|
||||||
|
local GAP61=' '
|
||||||
|
local GAP74=' '
|
||||||
|
|
||||||
|
printf "${DA_ST}┌──────────────────────────────────────────────────────────────────────────────────────┐${DA_RST}\n"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}╭─╮${DA_SR} %s ${DA_HL}╭─╮${DA_SR} │${DA_RST}\n" "$GAP74"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}│ │${DA_SR} ${DA_DIM}%s${DA_SR} ┌──────┐ ${DA_HL}│ │${DA_SR} │${DA_RST}\n" "$T525"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}│ │${DA_SR} ${DA_DIM}%s${DA_SR} │ ${DA_HL}(o)${DA_SR} │ ${DA_HL}│ │${DA_SR} │${DA_RST}\n" "$M525"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}│ │${DA_SR} ${DA_DIM}%s${DA_SR} │ │ ${DA_HL}│ │${DA_SR} │${DA_RST}\n" "$B525"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}│ │${DA_SR} %s │ ${DA_BLU}[A]${DA_SR} │ ${DA_HL}│ │${DA_SR} │${DA_RST}\n" "$GAP61"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}│ │${DA_SR} ${DA_HL}%s${DA_SR} │ ${DA_BLU}[A]${DA_SR} │ ${DA_HL}│ │${DA_SR} │${DA_RST}\n" "$TBAY"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}│ │${DA_SR} ${DA_HL}│${DA_SR}%s${DA_HL}│${DA_SR} │ ${DA_BLU}[C]${DA_SR} │ ${DA_HL}│ │${DA_SR} │${DA_RST}\n" "$fillrow"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}│ │${DA_SR} ${DA_HL}│${DA_SR}%s${DA_HL}│${DA_SR} │ │ ${DA_HL}│ │${DA_SR} │${DA_RST}\n" "$fillrow"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}│ │${DA_SR} ${DA_HL}│${DA_SR}%s${DA_HL}│${DA_SR} │ ${DA_DIM}(-)${DA_SR} │ ${DA_HL}│ │${DA_SR} │${DA_RST}\n" "$fillrow"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}│ │${DA_SR} ${DA_HL}│${DA_SR}%s${DA_HL}│${DA_SR} │ │ ${DA_HL}│ │${DA_SR} │${DA_RST}\n" "$fillrow"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}│ │${DA_SR} ${DA_HL}│${DA_SR}%s${DA_HL}│${DA_SR} │ │ ${DA_HL}│ │${DA_SR} │${DA_RST}\n" "$fillrow"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}│ │${DA_SR} ${DA_HL}│${DA_SR}%s${DA_HL}│${DA_SR} │ │ ${DA_HL}│ │${DA_SR} │${DA_RST}\n" "$fillrow"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}│ │${DA_SR} ${DA_HL}%s${DA_SR} └──────┘ ${DA_HL}│ │${DA_SR} │${DA_RST}\n" "$BBAY"
|
||||||
|
printf "${DA_ST}│ ${DA_HL}╰─╯${DA_SR} %s ${DA_HL}╰─╯${DA_SR} │${DA_RST}\n" "$GAP74"
|
||||||
|
printf "${DA_ST}└──────────────────────────────────────────────────────────────────────────────────────┘${DA_RST}\n"
|
||||||
|
|
||||||
|
# ---- captions, aligned under the ten slots ------------------------------
|
||||||
|
printf "${DA_ST} BAY %s${DA_ST} ${DA_RST}\n" "$numrow"
|
||||||
|
printf "${DA_ST} DEV %s${DA_ST} ${DA_RST}\n" "$namerow"
|
||||||
|
printf '\n'
|
||||||
|
|
||||||
|
# ---- off-panel drives + the one thing you must know ---------------------
|
||||||
|
local off='' v
|
||||||
|
[ -n "${DRIVE_MAP[m2-1]:-}" ] && off="M.2 ${DRIVE_MAP[m2-1]}"
|
||||||
|
for i in 1 2 3 4 5 6 7 8; do
|
||||||
|
v="${DRIVE_MAP[int-$i]:-}"
|
||||||
|
[ -n "$v" ] || continue
|
||||||
|
[ -n "$off" ] && off+=" "
|
||||||
|
off+="internal $v"
|
||||||
|
done
|
||||||
|
[ -n "$off" ] || off='none'
|
||||||
|
printf -v off '%-66.66s' "$off"
|
||||||
|
printf "${DA_ST} not on front face ${DA_CYN}%s${DA_RST}\n" "$off"
|
||||||
|
printf -v tail '%-88.88s' ' No backplane and no per-bay LEDs: every bay is a hand-cabled SFF-8482 to SATA lead.'
|
||||||
|
printf "${DA_DIM}%s${DA_RST}\n" "$tail"
|
||||||
|
printf -v tail '%-88.88s' ' Bay numbers are a LOCAL left-to-right convention - identify a drive by SERIAL.'
|
||||||
|
printf "${DA_DIM}%s${DA_RST}\n" "$tail"
|
||||||
|
|
||||||
|
if declare -F get_storage_controllers >/dev/null 2>&1; then
|
||||||
|
printf '\n'
|
||||||
|
printf " ${DA_HL}STORAGE CONTROLLERS${DA_RST}\n"
|
||||||
|
get_storage_controllers 2>/dev/null | sed 's/^/ /'
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user