Merge feature/pbs-nuc-drive-map: revamped PBS/NUC art + post-outage bay remap
Lint / Shell (shellcheck) (push) Failing after 6s
Lint / Shell (shellcheck) (push) Failing after 6s
This commit is contained in:
+182
-32
@@ -575,34 +575,174 @@ generate_micro_layout() {
|
||||
#
|
||||
# Side effects: Calls build_drive_map() to populate DRIVE_MAP
|
||||
#------------------------------------------------------------------------------
|
||||
# ══ driveAtlas :: color + glyph helpers ═══════════════════════════════════════
|
||||
|
||||
# da_repeat GLYPH N -- print GLYPH N times (multibyte-safe)
|
||||
da_repeat() {
|
||||
local out='' i
|
||||
for ((i = 0; i < $2; i++)); do out+="$1"; done
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
# da_color_init -- set the driveAtlas "hardware schematic" palette globals.
|
||||
# Structure is steel grey; color encodes STATE only (green=ok, amber/yellow,
|
||||
# red=hot, blue=cool). All vars are empty strings when color is off, so
|
||||
# rendering is byte-identical to pure monochrome.
|
||||
# USE_COLOR=true force on (depth still auto-picked)
|
||||
# USE_COLOR=false force off
|
||||
# unset/auto auto: off if NO_COLOR set, stdout not a tty,
|
||||
# or terminal reports < 256 colors
|
||||
# Depth: truecolor only when COLORTERM=truecolor/24bit, else 256, else 16.
|
||||
da_color_init() {
|
||||
DA_ST='' DA_HL='' DA_DIM='' DA_OK='' DA_YEL='' DA_HOT='' DA_BLU='' DA_CYN=''
|
||||
DA_SR='' DA_RST=''
|
||||
local mode="${USE_COLOR:-auto}" ncolors
|
||||
ncolors="$(tput colors 2>/dev/null)"; ncolors="${ncolors:-0}"
|
||||
case "$mode" in
|
||||
false) return 0 ;;
|
||||
auto)
|
||||
[ -n "${NO_COLOR-}" ] && return 0
|
||||
[ ! -t 1 ] && return 0
|
||||
[ "$ncolors" -lt 256 ] 2>/dev/null && return 0
|
||||
;;
|
||||
esac
|
||||
if [ "${COLORTERM-}" = "truecolor" ] || [ "${COLORTERM-}" = "24bit" ]; then
|
||||
DA_ST=$'\033[38;2;138;144;151m' # steel mid #8a9097
|
||||
DA_HL=$'\033[38;2;200;205;211m' # steel bright #c8cdd3
|
||||
DA_DIM=$'\033[38;2;86;93;102m' # steel shadow #565d66
|
||||
DA_OK=$'\033[38;2;51;209;122m' # LED green #33d17a
|
||||
DA_YEL=$'\033[38;2;246;196;69m' # LED amber #f6c445
|
||||
DA_HOT=$'\033[38;2;255;77;77m' # LED red #ff4d4d
|
||||
DA_BLU=$'\033[38;2;77;184;255m' # cool blue #4db8ff
|
||||
DA_CYN=$'\033[38;2;77;216;230m' # cyan accent #4dd8e6
|
||||
elif [ "$ncolors" -ge 256 ] 2>/dev/null; then
|
||||
DA_ST=$'\033[38;5;245m' DA_HL=$'\033[38;5;252m' DA_DIM=$'\033[38;5;238m'
|
||||
DA_OK=$'\033[38;5;40m' DA_YEL=$'\033[38;5;214m' DA_HOT=$'\033[38;5;196m'
|
||||
DA_BLU=$'\033[38;5;39m' DA_CYN=$'\033[38;5;51m'
|
||||
else
|
||||
DA_ST=$'\033[37m' DA_HL=$'\033[1;37m' DA_DIM=$'\033[90m'
|
||||
DA_OK=$'\033[32m' DA_YEL=$'\033[33m' DA_HOT=$'\033[31m'
|
||||
DA_BLU=$'\033[34m' DA_CYN=$'\033[36m'
|
||||
fi
|
||||
DA_RST=$'\033[0m'
|
||||
DA_SR="${DA_RST}${DA_ST}" # "return to structure": reset attrs, steel mid
|
||||
}
|
||||
|
||||
# da_gauge PCT WIDTH -- smooth eighth-block capacity gauge over a dim track,
|
||||
# e.g. "da_gauge 62 15" -> "████████▋░░░░░░". Color it by level at call site
|
||||
# (DA_OK < 70, DA_YEL < 90, DA_HOT >= 90); pad-then-color as usual.
|
||||
da_gauge() {
|
||||
local pct="${1:-0}" w="${2:-10}" full rem cells out='' i
|
||||
local eighth=('' '▏' '▎' '▍' '▌' '▋' '▊' '▉')
|
||||
[ "$pct" -lt 0 ] 2>/dev/null && pct=0
|
||||
[ "$pct" -gt 100 ] 2>/dev/null && pct=100
|
||||
full=$(( pct * w / 100 )); rem=$(( (pct * w * 8 / 100) % 8 )); cells=$full
|
||||
for ((i = 0; i < full; i++)); do out+='█'; done
|
||||
if [ "$rem" -gt 0 ] && [ "$full" -lt "$w" ]; then
|
||||
out+="${eighth[rem]}"; cells=$((cells + 1))
|
||||
fi
|
||||
for ((i = cells; i < w; i++)); do out+='░'; done
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
# ══ driveAtlas :: NUC5i5RYB/RYH chassis + drive-slot map ══════════════════════
|
||||
# 88-col cabinet-projection rendering with live device names from the global
|
||||
# associative array DRIVE_MAP (keys: int-os usb-fl usb-fr usb-rt usb-rb).
|
||||
# Occupied ports: solid light boxes, green LED bar + device. EMPTY ports:
|
||||
# dashed ghost boxes, dim. Alignment: every variable field is padded to its
|
||||
# final width BEFORE color codes wrap it; borders are literal strings.
|
||||
generate_nuc_layout() {
|
||||
local hostname="$1"
|
||||
build_drive_map
|
||||
local host="${1:-unknown}"
|
||||
local model=""
|
||||
if command -v maybe_sudo >/dev/null 2>&1; then
|
||||
model="$(maybe_sudo dmidecode -s baseboard-product-name 2>/dev/null | head -n1)"
|
||||
fi
|
||||
[ -n "$model" ] || model="NUC5i5RYB/RYH"
|
||||
|
||||
local nuc_model
|
||||
nuc_model="$(maybe_sudo dmidecode -s baseboard-product-name 2>/dev/null | head -n1)"
|
||||
[[ -z "$nuc_model" ]] && nuc_model="Intel NUC"
|
||||
da_color_init
|
||||
|
||||
printf "┌─────────────────────────────────────────────────────────────┐\n"
|
||||
printf "│ %-57s │\n" "$hostname - $nuc_model (Mini PC)"
|
||||
printf "│ │\n"
|
||||
printf "│ Storage Controllers: │\n"
|
||||
while IFS= read -r ctrl; do
|
||||
[[ -n "$ctrl" ]] && printf "│ %-59.59s│\n" "$ctrl"
|
||||
done < <(get_storage_controllers)
|
||||
printf "│ │\n"
|
||||
printf "│ Internal 2.5\" SATA (OS / boot): │\n"
|
||||
printf "│ ┌────────────────┐ │\n"
|
||||
printf "│ │ %-14s │ │\n" "${DRIVE_MAP[int-os]:-EMPTY}"
|
||||
printf "│ └────────────────┘ │\n"
|
||||
printf "│ │\n"
|
||||
printf "│ Rear USB 3.0 (stacked): │\n"
|
||||
printf "│ ┌─────────────────────────┐ │\n"
|
||||
printf "│ │ TOP (3-4): %-9s │ │\n" "${DRIVE_MAP[usb-top]:-EMPTY}"
|
||||
printf "│ ├─────────────────────────┤ │\n"
|
||||
printf "│ │ BOTTOM (3-3): %-9s │ │\n" "${DRIVE_MAP[usb-bot]:-EMPTY}"
|
||||
printf "│ └─────────────────────────┘ │\n"
|
||||
printf "└─────────────────────────────────────────────────────────────┘\n"
|
||||
# fixed-width header fields (pad/truncate so the frame never moves)
|
||||
local hostp modelp
|
||||
printf -v hostp '%-22.22s' "$host"
|
||||
printf -v modelp '%-14.14s' "$model"
|
||||
|
||||
# per-USB-slot pieces: device field (6 cols: LED bar + 5-char name),
|
||||
# box side glyph, label color, border dash -- ghost styling for EMPTY
|
||||
local fl fr rt rb io fls frs rts rbs flc frc rtc rbc fld frd rtd rbd
|
||||
local flt frt flb frb rtt rbb
|
||||
local pre key col v p
|
||||
for pre in fl:usb-fl:B fr:usb-fr:Y rt:usb-rt:B rb:usb-rb:B; do
|
||||
key="${pre#*:}"; key="${key%:*}"; col="${pre##*:}"; pre="${pre%%:*}"
|
||||
v="${DRIVE_MAP[$key]:-EMPTY}"
|
||||
if [ "$v" = "EMPTY" ]; then
|
||||
printf -v p ' %-5.5s' "$v"
|
||||
printf -v "$pre" '%s' "${DA_DIM}${p}${DA_SR}"
|
||||
printf -v "${pre}s" '%s' "${DA_DIM}╎${DA_SR}"
|
||||
printf -v "${pre}c" '%s' "${DA_DIM}"
|
||||
printf -v "${pre}d" '%s' '╌'
|
||||
else
|
||||
printf -v p '%-5.5s' "$v"
|
||||
printf -v "$pre" '%s' "${DA_OK}▌${p}${DA_SR}"
|
||||
printf -v "${pre}s" '%s' '│'
|
||||
[ "$col" = "Y" ] && printf -v "${pre}c" '%s' "${DA_YEL}" \
|
||||
|| printf -v "${pre}c" '%s' "${DA_BLU}"
|
||||
printf -v "${pre}d" '%s' '─'
|
||||
fi
|
||||
done
|
||||
# box edges (11 cols; front tops carry the callout stem junction)
|
||||
flt="$(da_repeat "$fld" 5)┴$(da_repeat "$fld" 5)"
|
||||
frt="┴$(da_repeat "$frd" 10)"
|
||||
flb="$(da_repeat "$fld" 11)"; frb="$(da_repeat "$frd" 11)"
|
||||
rtt="$(da_repeat "$rtd" 11)"; rbb="$(da_repeat "$rbd" 11)"
|
||||
[ "$fld" = '╌' ] && { flt="${DA_DIM}${flt}${DA_SR}"; flb="${DA_DIM}${flb}${DA_SR}"; }
|
||||
[ "$frd" = '╌' ] && { frt="${DA_DIM}${frt}${DA_SR}"; frb="${DA_DIM}${frb}${DA_SR}"; }
|
||||
[ "$rtd" = '╌' ] && rtt="${DA_DIM}${rtt}${DA_SR}"
|
||||
[ "$rbd" = '╌' ] && rbb="${DA_DIM}${rbb}${DA_SR}"
|
||||
# internal 2.5" SATA (int-os)
|
||||
v="${DRIVE_MAP[int-os]:-EMPTY}"
|
||||
if [ "$v" = "EMPTY" ]; then
|
||||
printf -v p ' %-5.5s' "$v"; io="${DA_DIM}${p}${DA_SR}"
|
||||
else
|
||||
printf -v p '%-5.5s' "$v"; io="${DA_OK}▌${p}${DA_SR}"
|
||||
fi
|
||||
|
||||
printf "${DA_ST}${DA_HL}┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓${DA_SR}${DA_RST}\n"
|
||||
printf "${DA_ST}${DA_HL}┃${DA_SR} ${DA_CYN}driveAtlas${DA_SR} :: ${DA_HL}%s${DA_SR} %s - 115x111x49 mm - tall (RYH) kit ${DA_HL}┃${DA_SR}${DA_RST}\n" "$hostp" "$modelp"
|
||||
printf "${DA_ST}${DA_HL}┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛${DA_SR}${DA_RST}\n"
|
||||
printf " \n"
|
||||
printf "${DA_ST} FRONT 3/4 ── cool intake side REAR ── hot exhaust side ${DA_RST}\n"
|
||||
printf " \n"
|
||||
printf "${DA_ST} ${DA_DIM}▄▄▄▄▄▄ ▄▄▄▄▄▄${DA_SR} < CPU fan vents ${DA_RST}\n"
|
||||
printf "${DA_ST} ${DA_HOT} v v v hot air${DA_SR} ${DA_RST}\n"
|
||||
printf "${DA_ST} ${DA_HL}┌──────────────────────────┐${DA_SR} ${DA_HL}╭────────────────────────────────╮${DA_SR} ${DA_RST}\n"
|
||||
printf "${DA_ST} ${DA_HL}╱${DA_SR}${DA_DIM}░░░─░░░░░░░─░░░░░░░░─░░░░░${DA_SR}${DA_HL}╱│${DA_SR} ${DA_HL}│${DA_SR} ┌%s┐ ${DA_HL}│${DA_SR} ${DA_RST}\n" "$rtt"
|
||||
printf "${DA_ST} ${DA_HL}╱${DA_SR}${DA_DIM}░${DA_SR}${DA_OK}(o)${DA_SR} pwr ${DA_DIM}░░─░░░░░─░░░░░░░─${DA_SR}${DA_HL}╱${DA_SR}${DA_DIM}▒${DA_SR}${DA_HL}│${DA_SR} ${DA_HL}│${DA_SR} (o) ▐═▌ ▐╬▌ %s %s3-4 BLUE${DA_SR} %s ▐▄▌ ${DA_HL}│${DA_SR} ${DA_RST}\n" "$rts" "$rtc" "$rts"
|
||||
printf "${DA_ST} ${DA_HL}┌──────────────────────────┐${DA_SR}${DA_DIM}▒▒${DA_SR}${DA_HL}│${DA_SR} ${DA_HL}│${DA_SR} DC mDP GbE %s %s %smHDMI${DA_HL}│${DA_SR} ${DA_RST}\n" "$rts" "$rt" "$rts"
|
||||
printf "${DA_ST} ${DA_HL}│${DA_SR} ╌ brushed aluminum band ╌${DA_HL}│${DA_SR}${DA_DIM}▒${DA_SR}${DA_HL}╱${DA_SR} ${DA_HL}│${DA_SR} ├───────────┤ ${DA_HL}│${DA_SR} ${DA_RST}\n"
|
||||
printf "${DA_ST} ${DA_HL}│${DA_SR} ${DA_BLU}▐███▌${DA_SR} ${DA_YEL}▐███▌${DA_SR} (o) . ${DA_HL}│╱${DA_SR} ${DA_HL}│${DA_SR} %s %s3-3 BLUE${DA_SR} %s ${DA_HL}│${DA_SR}${DA_HOT}< bakes in${DA_SR} ${DA_RST}\n" "$rbs" "$rbc" "$rbs"
|
||||
printf "${DA_ST} ${DA_HL}└────┬───────┬─────────────┘${DA_SR} ${DA_HL}│${DA_SR} %s %s %s ${DA_HL}│${DA_SR}${DA_HOT} CPU exhaust${DA_SR}${DA_RST}\n" "$rbs" "$rb" "$rbs"
|
||||
printf "${DA_ST} │ │ audio IR ${DA_HL}│${DA_SR} └%s┘ ${DA_HL}│${DA_SR} ${DA_RST}\n" "$rbb"
|
||||
printf "${DA_ST} ┌%s┐┌%s┐ ${DA_HL}╰────────────────────────────────╯${DA_SR} ${DA_RST}\n" "$flt" "$frt"
|
||||
printf "${DA_ST} %s %s3-2 BLUE${DA_SR} %s%s%s3-1? YELLOW${DA_SR}%s ${DA_DIM}▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀${DA_SR} ${DA_RST}\n" "$fls" "$flc" "$fls" "$frs" "$frc" "$frs"
|
||||
printf "${DA_ST} %s %s %s%s %s %s ${DA_RST}\n" "$fls" "$fl" "$fls" "$frs" "$fr" "$frs"
|
||||
printf "${DA_ST} %s USB3 data %s%s charging %s ${DA_RST}\n" "$fls" "$fls" "$frs" "$frs"
|
||||
printf "${DA_ST} └%s┘└%s┘ ${DA_RST}\n" "$flb" "$frb"
|
||||
printf " \n"
|
||||
printf "${DA_ST} ${DA_HL}UNDERSIDE / INSIDE${DA_SR} ${DA_HL}NOTES${DA_SR} ${DA_RST}\n"
|
||||
printf "${DA_ST} ┌──────────────────────────────────┐ ${DA_RST}\n"
|
||||
printf "${DA_ST} │ internal 2.5\" SATA ── mounted │ - all 4 USB3 jacks hang off ONE xHCI ${DA_RST}\n"
|
||||
printf "${DA_ST} │ INVERTED in the bottom lid │ controller ── one shared kernel bus ${DA_RST}\n"
|
||||
printf "${DA_ST} │ %s < int-os - OS / boot │ - M.2 2280 slot ── flat on mainboard ${DA_RST}\n" "$io"
|
||||
printf "${DA_ST} │${DA_DIM} ╌╌╌╌╌ flex cable to mainboard ╌╌ ${DA_SR}│ - \"?\" = port 3-1 id inferred (unverified) ${DA_RST}\n"
|
||||
printf "${DA_ST} └──────────────────────────────────┘ ${DA_RST}\n"
|
||||
printf "${DA_ST} ${DA_DIM}o feet o────── VESA ──────o feet o${DA_SR} ${DA_RST}\n"
|
||||
|
||||
# optional flourish: controller inventory from the existing helper
|
||||
if command -v get_storage_controllers >/dev/null 2>&1; then
|
||||
printf '\n %sSTORAGE CONTROLLERS%s\n' "${DA_HL}" "${DA_RST}"
|
||||
get_storage_controllers 2>/dev/null | sed "s/^/ /"
|
||||
fi
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -778,18 +918,28 @@ declare -A SERVER_MAPPINGS=(
|
||||
# pbs
|
||||
# Intel NUC5i5RYB - Proxmox Backup Server
|
||||
# int-os : internal 2.5\" SATA SSD (Samsung MZNLN128) - OS/boot, controller 00:1f.2
|
||||
# usb-top : TOP rear USB 3.0 NVMe (Patriot P300 / JMicron bridge) - halfTbNVMeMirror
|
||||
# usb-bot : BOTTOM rear USB 3.0 NVMe (Patriot P300 / JMicron bridge) - halfTbNVMeMirror
|
||||
# usb-fl : FRONT-LEFT USB 3.0 (blue) NVMe (Patriot P300 / JMicron bridge) - halfTbNVMeMirror
|
||||
# usb-fr : FRONT-RIGHT USB 3.0 (yellow, always-on charging) - normally empty
|
||||
# usb-rt : REAR-TOP USB 3.0 (blue) - now empty
|
||||
# usb-rb : REAR-BOTTOM USB 3.0 (blue) NVMe (Patriot P300 / JMicron bridge) - halfTbNVMeMirror
|
||||
#
|
||||
# The two USB bridges report an IDENTICAL serial, so these drives are keyed by USB
|
||||
# *port path* (usb-0:N = kernel bus3 port N), NOT by-id/serial. Physical top/bottom
|
||||
# confirmed by an LED blink test on 2026-07-15:
|
||||
# TOP rear jack = port 3-4 (usb-0:4) = fw serial P300WCBA24090617615 (label 022411042502235)
|
||||
# BOTTOM rear jack = port 3-3 (usb-0:3) = fw serial P300WCBA24090617536
|
||||
# *port path* (usb-0:N = kernel bus3 port N), NOT by-id/serial.
|
||||
# Port jacks (LED-blink verified 2026-07-15; front-left re-verified 2026-07-18):
|
||||
# REAR-TOP jack = port 3-4 (usb-0:4) [blue]
|
||||
# REAR-BOTTOM jack = port 3-3 (usb-0:3) [blue] = fw serial P300WCBA24090617536
|
||||
# FRONT-LEFT jack = port 3-2 (usb-0:2) [blue] = fw serial P300WCBA24090617615
|
||||
# FRONT-RIGHT jack = port 3-1 (usb-0:1) [yellow] (port # unverified - jack empty)
|
||||
#
|
||||
# 2026-07-18: after a power outage the ...615 drive dropped to 0B / hardware-error in the
|
||||
# hot stacked REAR-TOP bay; moving it to FRONT-LEFT (3-2) revived it. Root cause was
|
||||
# thermal - the two NVMe USB bridges sat flush against each other in the rear stack.
|
||||
["pbs"]="
|
||||
pci-0000:00:1f.2-ata-4 int-os
|
||||
pci-0000:00:14.0-usb-0:4:1.0-scsi-0:0:0:0 usb-top
|
||||
pci-0000:00:14.0-usb-0:3:1.0-scsi-0:0:0:0 usb-bot
|
||||
pci-0000:00:14.0-usb-0:2:1.0-scsi-0:0:0:0 usb-fl
|
||||
pci-0000:00:14.0-usb-0:1:1.0-scsi-0:0:0:0 usb-fr
|
||||
pci-0000:00:14.0-usb-0:4:1.0-scsi-0:0:0:0 usb-rt
|
||||
pci-0000:00:14.0-usb-0:3:1.0-scsi-0:0:0:0 usb-rb
|
||||
"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user