Hardware discovered: - LSI SAS3008 HBA at 01:00.0 (bays 5-10 via mini-SAS HD cables) - AMD SATA controller at 0d:00.0 (bays 1-4) - NVMe at 0e:00.0 (M.2 slot) Changes: - Updated SERVER_MAPPINGS with correct PCI paths based on actual hardware - Fixed diagnose-drives.sh CRLF line endings (was causing script errors) - Updated README with accurate controller information - Mapped all 10 bays plus M.2 NVMe slot - Added detailed cable mapping comments from user documentation The old mapping referenced non-existent controller 0c:00.0. Now uses actual SAS PHY paths and ATA port numbers that match physical bays. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
276 lines
12 KiB
Bash
276 lines
12 KiB
Bash
#!/bin/bash
|
|
|
|
#==============================================================================
|
|
# Drive Atlas - Server Drive Mapping Tool
|
|
# Maps physical drive bays to logical device names using PCI paths
|
|
#==============================================================================
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Chassis Type Definitions
|
|
# These define the physical layout and display formatting for each chassis type
|
|
#------------------------------------------------------------------------------
|
|
|
|
generate_10bay_layout() {
|
|
local hostname=$1
|
|
build_drive_map
|
|
|
|
# Calculate max width needed for drive names
|
|
max_width=0
|
|
for bay in {1..10} "m2-1" "usb1" "usb2"; do
|
|
drive_text="${DRIVE_MAP[$bay]:-EMPTY}"
|
|
text_len=$((${#bay} + 1 + ${#drive_text}))
|
|
[[ $text_len -gt $max_width ]] && max_width=$text_len
|
|
done
|
|
|
|
# Add padding for box borders
|
|
box_width=$((max_width + 4))
|
|
|
|
# Create box drawing elements
|
|
h_line=$(printf '%*s' "$box_width" '' | tr ' ' '─')
|
|
|
|
# USB Section (if applicable)
|
|
if [[ -n "${DRIVE_MAP[usb1]}" || -n "${DRIVE_MAP[usb2]}" ]]; then
|
|
printf "\n External USB\n"
|
|
printf " ┌%s┐ ┌%s┐\n" "$h_line" "$h_line"
|
|
printf " │ %-${max_width}s │ │ %-${max_width}s │\n" "${DRIVE_MAP[usb1]:-EMPTY}" "${DRIVE_MAP[usb2]:-EMPTY}"
|
|
printf " └%s┘ └%s┘\n\n" "$h_line" "$h_line"
|
|
fi
|
|
|
|
# Main chassis section
|
|
printf "┌──────────────────────────────────────────────────────────────┐\n"
|
|
printf "│ %-58s │\n" "$hostname"
|
|
printf "│ %-58s │\n" "10-Bay Hot-swap Chassis"
|
|
printf "│ │\n"
|
|
|
|
# M.2 NVMe slot if present
|
|
if [[ -n "${DRIVE_MAP[m2-1]}" ]]; then
|
|
printf "│ M.2 NVMe Slot │\n"
|
|
printf "│ ┌%s┐ │\n" "$h_line"
|
|
printf "│ │ %-${max_width}s │ │\n" "${DRIVE_MAP[m2-1]:-EMPTY}"
|
|
printf "│ └%s┘ │\n" "$h_line"
|
|
printf "│ │\n"
|
|
fi
|
|
|
|
printf "│ Front Hot-swap Bays │\n"
|
|
|
|
# Create bay rows
|
|
printf "│ "
|
|
for bay in {1..10}; do
|
|
printf "┌%s┐" "$h_line"
|
|
done
|
|
printf " │\n│ "
|
|
|
|
for bay in {1..10}; do
|
|
printf "│%-2d:%-${max_width}s │" "$bay" "${DRIVE_MAP[$bay]:-EMPTY}"
|
|
done
|
|
printf " │\n│ "
|
|
|
|
for bay in {1..10}; do
|
|
printf "└%s┘" "$h_line"
|
|
done
|
|
printf " │\n"
|
|
|
|
printf "└──────────────────────────────────────────────────────────────┘\n"
|
|
}
|
|
|
|
generate_large1_layout() {
|
|
build_drive_map
|
|
|
|
cat << 'EOF'
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ large1 │
|
|
│ Unique 3x5 Grid Chassis │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────┐ │
|
|
│ │ Motherboard │ │
|
|
│ │ │ │
|
|
│ │ ┌──┐┌──┐ │ │
|
|
│ │ │M1││M2│ │ │
|
|
│ │ └──┘└──┘ │ │
|
|
│ └──────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ 1 │ │ 2 │ │ 3 │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
|
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ 4 │ │ 5 │ │ 6 │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
|
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
|
│ │ │ │ │ │ │ │
|
|
│ │ 7 │ │ 8 │ │ 9 │ │
|
|
│ │ │ │ │ │ │ │
|
|
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
EOF
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Server-Specific Drive Mappings
|
|
# Maps PCI paths to physical bay numbers for each server
|
|
# Format: "pci-path bay-number"
|
|
#------------------------------------------------------------------------------
|
|
|
|
declare -A SERVER_MAPPINGS=(
|
|
# compute-storage-01 (formerly medium2)
|
|
# Motherboard: B650D4U3-2Q/BCM with AMD SATA controller
|
|
# HBA: LSI SAS3008 at 01:00.0 (mini-SAS HD ports)
|
|
# Cable mapping from user notes:
|
|
# - Mobo SATA: top-right=bay1, bottom-right=bay2, bottom-left=bay3, top-left=bay4
|
|
# - HBA bottom mini-SAS: bays 5,6,7,8
|
|
# - HBA top mini-SAS: bays 9,10
|
|
["compute-storage-01"]="
|
|
pci-0000:0d:00.0-ata-3 1
|
|
pci-0000:0d:00.0-ata-4 2
|
|
pci-0000:0d:00.0-ata-2 3
|
|
pci-0000:0d:00.0-ata-1 4
|
|
pci-0000:01:00.0-sas-phy2-lun-0 5
|
|
pci-0000:01:00.0-sas-phy3-lun-0 6
|
|
pci-0000:01:00.0-sas-phy4-lun-0 7
|
|
pci-0000:01:00.0-sas-phy5-lun-0 8
|
|
pci-0000:01:00.0-sas-phy6-lun-0 9
|
|
pci-0000:01:00.0-sas-phy7-lun-0 10
|
|
pci-0000:0e:00.0-nvme-1 m2-1
|
|
"
|
|
|
|
# storage-01
|
|
# Different motherboard, no HBA currently
|
|
# TODO: Map actual PCI paths after running diagnose-drives.sh
|
|
["storage-01"]="
|
|
"
|
|
|
|
# large1
|
|
# Unique chassis - 1/1 configuration
|
|
# TODO: Map actual PCI paths after running diagnose-drives.sh
|
|
["large1"]="
|
|
"
|
|
)
|
|
|
|
declare -A CHASSIS_TYPES=(
|
|
["compute-storage-01"]="10bay"
|
|
["compute-storage-gpu-01"]="10bay"
|
|
["storage-01"]="10bay"
|
|
["large1"]="large1"
|
|
["micro1"]="micro"
|
|
["monitor-02"]="micro"
|
|
)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Core Functions
|
|
#------------------------------------------------------------------------------
|
|
|
|
build_drive_map() {
|
|
local host=$(hostname)
|
|
declare -A drive_map
|
|
|
|
local mapping=${SERVER_MAPPINGS[$host]}
|
|
|
|
if [[ -n "$mapping" ]]; then
|
|
while read -r path slot; do
|
|
[[ -z "$path" || -z "$slot" ]] && continue
|
|
|
|
if [[ -L "/dev/disk/by-path/$path" ]]; then
|
|
local drive=$(readlink -f "/dev/disk/by-path/$path" | sed 's/.*\///')
|
|
drive_map[$slot]=$drive
|
|
fi
|
|
done <<< "$mapping"
|
|
fi
|
|
|
|
# Make drive_map available globally
|
|
declare -g -A DRIVE_MAP=()
|
|
for key in "${!drive_map[@]}"; do
|
|
DRIVE_MAP[$key]=${drive_map[$key]}
|
|
done
|
|
}
|
|
|
|
get_drive_smart_info() {
|
|
local device=$1
|
|
local smart_info=$(sudo smartctl -A -i -H /dev/$device 2>/dev/null)
|
|
local temp=$(echo "$smart_info" | grep "Temperature" | awk '{print $10}' | head -1)
|
|
local type=$(echo "$smart_info" | grep "Rotation Rate" | grep -q "Solid State" && echo "SSD" || echo "HDD")
|
|
local health=$(echo "$smart_info" | grep "SMART overall-health" | grep -q "PASSED" && echo "✓" || echo "✗")
|
|
local model=$(echo "$smart_info" | grep "Device Model\|Model Number" | cut -d: -f2 | xargs)
|
|
|
|
echo "$type|$temp°C|$health|$model"
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Main Display Logic
|
|
#------------------------------------------------------------------------------
|
|
|
|
HOSTNAME=$(hostname)
|
|
CHASSIS_TYPE=${CHASSIS_TYPES[$HOSTNAME]:-"unknown"}
|
|
|
|
# Display chassis layout
|
|
case "$CHASSIS_TYPE" in
|
|
"10bay")
|
|
generate_10bay_layout "$HOSTNAME"
|
|
;;
|
|
"large1")
|
|
generate_large1_layout
|
|
;;
|
|
"micro")
|
|
echo "Micro server layout not yet implemented"
|
|
;;
|
|
*)
|
|
echo "┌─────────────────────────────────────────────────────────┐"
|
|
echo "│ Unknown server: $HOSTNAME"
|
|
echo "│ No chassis mapping defined yet"
|
|
echo "│ Run diagnose-drives.sh to gather PCI path information"
|
|
echo "└─────────────────────────────────────────────────────────┘"
|
|
;;
|
|
esac
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Drive Details Section
|
|
#------------------------------------------------------------------------------
|
|
|
|
echo -e "\n=== Drive Details with SMART Status ==="
|
|
printf "%-15s %-10s %-8s %-8s %-8s %-30s\n" "DEVICE" "SIZE" "TYPE" "TEMP" "HEALTH" "MODEL"
|
|
echo "--------------------------------------------------------------------------------"
|
|
|
|
# SATA/SAS drives
|
|
lsblk -d -o NAME | grep -v "nvme" | grep -v "rbd" | grep -v "loop" | grep -v "NAME" | while read device; do
|
|
if [ -b "/dev/$device" ]; then
|
|
size=$(lsblk -d -n -o SIZE "/dev/$device" 2>/dev/null)
|
|
smart_info=$(get_drive_smart_info "$device")
|
|
IFS='|' read -r type temp health model <<< "$smart_info"
|
|
printf "%-15s %-10s %-8s %-8s %-8s %-30s\n" "/dev/$device" "$size" "$type" "$temp" "$health" "$model"
|
|
fi
|
|
done
|
|
|
|
# NVMe drives
|
|
if command -v nvme >/dev/null 2>&1; then
|
|
nvme_drives=$(sudo nvme list 2>/dev/null | grep "^/dev")
|
|
if [ -n "$nvme_drives" ]; then
|
|
echo -e "\n=== NVMe Drives ==="
|
|
printf "%-15s %-10s %-10s %-40s\n" "DEVICE" "SIZE" "TYPE" "MODEL"
|
|
echo "--------------------------------------------------------------------------------"
|
|
echo "$nvme_drives" | awk '{printf "%-15s %-10s %-10s %-40s\n", $1, $6, "NVMe", $3}'
|
|
fi
|
|
fi
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Optional sections
|
|
#------------------------------------------------------------------------------
|
|
|
|
# Ceph RBD Devices
|
|
rbd_output=$(lsblk -o NAME,SIZE,TYPE,MOUNTPOINT 2>/dev/null | grep "rbd" | sort -V)
|
|
if [ -n "$rbd_output" ]; then
|
|
echo -e "\n=== Ceph RBD Devices ==="
|
|
printf "%-15s %-10s %-10s %-20s\n" "DEVICE" "SIZE" "TYPE" "MOUNTPOINT"
|
|
echo "------------------------------------------------------------"
|
|
echo "$rbd_output"
|
|
fi
|
|
|
|
# Show mapping diagnostic info if DEBUG is set
|
|
if [[ -n "$DEBUG" ]]; then
|
|
echo -e "\n=== DEBUG: Drive Mappings ==="
|
|
for key in "${!DRIVE_MAP[@]}"; do
|
|
echo "Bay $key: ${DRIVE_MAP[$key]}"
|
|
done | sort -n
|
|
fi
|