Refactor Drive Atlas with modular chassis templates and PCI path mapping

Major improvements:
- Separated chassis types from server hostnames for better reusability
- Implemented template-based layout system (10bay, large1, spare-10bay)
- Renamed medium2 to compute-storage-01 for clarity
- Added comprehensive PCI path-based drive mapping system
- Created diagnose-drives.sh helper script for mapping new servers
- Added DEBUG mode for troubleshooting drive mappings
- Documented Sliger CX471225 4U chassis model

Technical changes:
- Replaced DRIVE_MAPPINGS with separate SERVER_MAPPINGS and CHASSIS_TYPES
- Improved drive detection and SMART data collection
- Better error handling for missing drives and unmapped servers
- Cleaner code structure with sectioned comments

Documentation:
- Complete rewrite of README with setup guide and troubleshooting
- Added detailed todo.txt with action plan and technical notes
- Included step-by-step instructions for mapping new servers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-06 15:52:24 -05:00
parent 585240b03f
commit 657b7d9a2d
4 changed files with 497 additions and 264 deletions

2
.gitignore vendored
View File

@@ -1,3 +1 @@
todo.txt todo.txt
medium1_notes.txt
medium2_notes.txt

255
README.md
View File

@@ -1,44 +1,241 @@
# Drive Atlas # Drive Atlas
A powerful server drive mapping tool that generates visual ASCII representations of server layouts and provides comprehensive drive information based on the server's hostname. A powerful server drive mapping tool that generates visual ASCII representations of server layouts and provides comprehensive drive information. Maps physical drive bays to logical Linux device names using PCI bus paths for reliable, persistent identification.
## Features ## Features
- 🖼️ Visual ASCII art maps for different server types (large1, medium1, medium2, micro1/2) - Visual ASCII art maps showing physical drive bay layouts
- 💽 Detailed NVMe drive information - Persistent drive identification using PCI paths (not device letters)
- 🔄 SATA drive listing with size and mount points - SMART health status and temperature monitoring
- 🔍 PCI Bus Device Function (BDF) details - Support for SATA, NVMe, and USB drives
- 📝 Drive identification by unique device IDs - Detailed drive information including model, size, and health status
- Per-server configuration for accurate physical-to-logical mapping
## Quick Start ## Quick Start
Execute remotely using either command: Execute remotely using curl:
```bash ```bash
bash <(curl -s http://10.10.10.110:3000/JWS/driveAtlas/raw/branch/main/driveAtlas.sh) bash <(curl -s http://10.10.10.63:3000/LotusGuild/driveAtlas/raw/branch/main/driveAtlas.sh)
``` ```
- Using `wget`:
```bash Or using wget:
bash <(wget -qO- http://10.10.10.110:3000/JWS/driveAtlas/raw/branch/main/driveAtlas.sh) ```bash
bash <(wget -qO- http://10.10.10.63:3000/LotusGuild/driveAtlas/raw/branch/main/driveAtlas.sh)
``` ```
## Requirements ## Requirements
Linux environment with bash
sudo privileges for NVMe operations
curl or wget for remote execution
## Server Types
The script supports different server layouts:
Type Description - Linux environment with bash
large1 3x3 grid layout (9 drives) - `sudo` privileges for SMART operations
medium1 2x4 + 2 layout (10 drives) - `smartctl` (from smartmontools package)
medium2 Single row layout (10 drives) - `lsblk` and `lspci` (typically pre-installed)
micro1/2 Compact 2-drive layout - Optional: `nvme-cli` for NVMe drives
## Server Configurations
### Chassis Types
| Chassis Type | Description | Servers Using It |
|-------------|-------------|------------------|
| **10-Bay Hot-swap** | Sliger CX471225 4U 10x 3.5" NAS (with unused 2x 5.25" bays) | compute-storage-01, storage-01 | compute-storage-gpu-01
| **Spare 10-Bay** | Some random chinese chassis |
| **Large1 Grid** | Unique 3x5 grid layout (1/1 configuration) | large1 |
| **Micro** | Compact 2-drive layout | micro1, monitor-02 |
### Server Details
#### compute-storage-01 (formerly medium2)
- **Chassis:** Sliger CX471225 4U (10-Bay Hot-swap)
- **Motherboard:** B650D4U3-2Q/BCM
- **Controllers:**
- 0c:00.0 - Front hot-swap bays
- 0d:00.0 - M.2 NVMe slot
- 0b:00.0 - USB controller
- **Status:** Partially mapped (bays 3-6 only)
#### storage-01
- **Chassis:** Sliger CX471225 4U (10-Bay Hot-swap)
- **Motherboard:** Different from compute-storage-01
- **Controllers:** Motherboard SATA only (no HBA currently)
- **Status:** Requires PCI path mapping
#### large1
- **Chassis:** Unique 3x5 grid (15 bays total)
- **Note:** 1/1 configuration, will not be replicated
- **Status:** Requires PCI path mapping
#### compute-storage-gpu-01
- **Chassis:** Sliger CX471225 4U (spare, not deployed)
- **Status:** Not currently in use
## How It Works
### PCI Path-Based Mapping
Drive Atlas uses `/dev/disk/by-path/` to create persistent mappings between physical drive bays and Linux device names. This is superior to using device letters (sda, sdb, etc.) which can change between boots.
**Example PCI path:**
```
pci-0000:0c:00.0-ata-1 → /dev/sda
```
This tells us:
- `0000:0c:00.0` - PCI bus address of the storage controller
- `ata-1` - Port 1 on that controller
- Maps to physical bay 3 on compute-storage-01
### Configuration
Server mappings are defined in the `SERVER_MAPPINGS` associative array in [driveAtlas.sh](driveAtlas.sh):
```bash
declare -A SERVER_MAPPINGS=(
["compute-storage-01"]="
pci-0000:0c:00.0-ata-1 3
pci-0000:0c:00.0-ata-2 4
pci-0000:0d:00.0-nvme-1 m2-1
"
)
```
## Setting Up a New Server
### Step 1: Run Diagnostic Script
First, gather PCI path information:
```bash
bash diagnose-drives.sh > server-diagnostic.txt
```
This will show all available PCI paths and their associated drives.
### Step 2: Physical Bay Identification
For each populated drive bay:
1. Note the physical bay number (labeled on chassis)
2. Identify a unique characteristic (size, model, or serial number)
3. Match it to the PCI path from the diagnostic output
**Pro tip:** If uncertain, remove one drive at a time and re-run the diagnostic to see which PCI path disappears.
### Step 3: Create Mapping
Add a new entry to `SERVER_MAPPINGS` in [driveAtlas.sh](driveAtlas.sh):
```bash
["your-hostname"]="
pci-0000:XX:XX.X-ata-1 1
pci-0000:XX:XX.X-ata-2 2
# ... etc
"
```
Also add the chassis type to `CHASSIS_TYPES`:
```bash
["your-hostname"]="10bay"
```
### Step 4: Test
Run the main script and verify the layout matches your physical configuration:
```bash
bash driveAtlas.sh
```
Use debug mode to see the mappings:
```bash
DEBUG=1 bash driveAtlas.sh
```
## Output Example ## Output Example
The script provides:
ASCII visualization of server layout ```
NVMe drive listing ┌──────────────────────────────────────────────────────────────┐
SATA drive information │ compute-storage-01 │
PCI BDF details │ 10-Bay Hot-swap Chassis │
Drive ID mappings │ │
│ M.2 NVMe Slot │
│ ┌──────────┐ │
│ │ nvme0n1 │ │
│ └──────────┘ │
│ │
│ Front Hot-swap Bays │
│ ┌──────────┐┌──────────┐┌──────────┐┌──────────┐... │
│ │1: EMPTY ││2: EMPTY ││3: sda ││4: sdb │... │
│ └──────────┘└──────────┘└──────────┘└──────────┘... │
└──────────────────────────────────────────────────────────────┘
=== Drive Details with SMART Status ===
DEVICE SIZE TYPE TEMP HEALTH MODEL
--------------------------------------------------------------------------------
/dev/sda 2TB HDD 35°C ✓ WD20EFRX-68EUZN0
/dev/nvme0n1 1TB SSD 42°C ✓ Samsung 980 PRO
```
## Troubleshooting
### Drive shows as EMPTY but is physically present
- Check if the drive is detected: `ls -la /dev/disk/by-path/`
- Verify the PCI path in the mapping matches the actual path
- Ensure the drive has power and SATA/power connections are secure
### PCI paths don't match between servers with "identical" hardware
- Even identical motherboards can have different PCI addressing
- BIOS settings can affect PCI enumeration
- HBA installation in different PCIe slots changes addresses
- Cable routing to different SATA ports changes the ata-N number
### SMART data not showing
- Ensure `smartmontools` is installed: `sudo apt install smartmontools`
- Some drives don't report temperature
- USB-connected drives may not support SMART
- Run `sudo smartctl -i /dev/sdX` manually to check
## Files
- [driveAtlas.sh](driveAtlas.sh) - Main script
- [diagnose-drives.sh](diagnose-drives.sh) - PCI path diagnostic tool
- [README.md](README.md) - This file
- [todo.txt](todo.txt) - Development notes
## Contributing
When adding support for a new server:
1. Run `diagnose-drives.sh` and save output
2. Physically label or identify drives
3. Create mapping in `SERVER_MAPPINGS`
4. Test thoroughly
5. Document any unique hardware configurations
6. Update this README
## Technical Notes
### Why PCI Paths?
Linux device names (sda, sdb, etc.) are assigned in discovery order, which can change:
- Between kernel versions
- After BIOS updates
- When drives are added/removed
- Due to timing variations at boot
PCI paths are deterministic and based on physical hardware topology.
### Bay Numbering Conventions
- **10-bay chassis:** Bays numbered 1-10 (left to right, top to bottom)
- **M.2 slots:** Labeled as `m2-1`, `m2-2`, etc.
- **USB drives:** Labeled as `usb1`, `usb2`, etc.
- **Large1:** Grid numbering 1-9 (3x3 displayed, additional bays documented in mapping)
## License
Internal tool for LotusGuild infrastructure.

59
diagnose-drives.sh Normal file
View File

@@ -0,0 +1,59 @@
#!/bin/bash
# Drive Atlas Diagnostic Script
# Run this on each server to gather PCI path information
echo "=== Server Information ==="
echo "Hostname: $(hostname)"
echo "Date: $(date)"
echo ""
echo "=== All /dev/disk/by-path/ entries ==="
ls -la /dev/disk/by-path/ | grep -v "part" | sort
echo ""
echo "=== Organized by PCI Address ==="
for path in /dev/disk/by-path/*; do
if [ -L "$path" ]; then
# Skip partitions
if [[ "$path" =~ -part[0-9]+$ ]]; then
continue
fi
basename_path=$(basename "$path")
target=$(readlink -f "$path")
device=$(basename "$target")
echo "Path: $basename_path"
echo " -> Device: $device"
# Try to get size
if [ -b "$target" ]; then
size=$(lsblk -d -n -o SIZE "$target" 2>/dev/null)
echo " -> Size: $size"
fi
# Try to get SMART info for model
if command -v smartctl >/dev/null 2>&1; then
model=$(sudo smartctl -i "$target" 2>/dev/null | grep "Device Model\|Model Number" | cut -d: -f2 | xargs)
if [ -n "$model" ]; then
echo " -> Model: $model"
fi
fi
echo ""
fi
done
echo "=== PCI Devices with Storage Controllers ==="
lspci | grep -i "storage\|raid\|sata\|sas\|nvme"
echo ""
echo "=== Current Block Devices ==="
lsblk -d -o NAME,SIZE,TYPE,TRAN | grep -v "rbd\|loop"
echo ""
echo "=== Recommendations ==="
echo "1. Note the PCI addresses (e.g., 0c:00.0) of your storage controllers"
echo "2. For each bay, physically identify which drive is in it"
echo "3. Match the PCI path pattern to the bay number"
echo "4. Example: pci-0000:0c:00.0-ata-1 might be bay 1 on controller 0c:00.0"

View File

@@ -1,84 +1,85 @@
#!/bin/bash #!/bin/bash
get_device_info() { #==============================================================================
local pci_addr=$1 # Drive Atlas - Server Drive Mapping Tool
local info=$(lspci -s "$pci_addr") # Maps physical drive bays to logical device names using PCI paths
echo "$info" #==============================================================================
}
get_drive_details() { #------------------------------------------------------------------------------
local device=$1 # Chassis Type Definitions
local size=$(lsblk -d -o NAME,SIZE | grep "$device" | awk '{print $2}') # These define the physical layout and display formatting for each chassis type
echo "$size" #------------------------------------------------------------------------------
}
get_drive_smart_info() { generate_10bay_layout() {
local device=$1 local hostname=$1
local smart_info=$(sudo smartctl -A -i -H /dev/$device 2>/dev/null) build_drive_map
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" | cut -d: -f2 | xargs)
echo "$type|$temp°C|$health|$model" # Calculate max width needed for drive names
} max_width=0
for bay in {1..10} "m2-1" "usb1" "usb2"; do
get_drives_info() { drive_text="${DRIVE_MAP[$bay]:-EMPTY}"
local path="/dev/disk/by-path" text_len=$((${#bay} + 1 + ${#drive_text}))
for drive in "$path"/*; do [[ $text_len -gt $max_width ]] && max_width=$text_len
if [ -L "$drive" ]; then
echo "$(basename "$drive") $(readlink -f "$drive")"
fi
done done
}
declare -A DRIVE_MAPPINGS=( # Add padding for box borders
["medium2"]=" box_width=$((max_width + 4))
pci-0000:0c:00.0-ata-3 5
pci-0000:0c:00.0-ata-4 6
pci-0000:0c:00.0-ata-1 3
pci-0000:0c:00.0-ata-2 4
pci-0000:0d:00.0-nvme-1 11
pci-0000:0b:00.0-usb-0:3:1.0-scsi-0:0:0:0 usb1
pci-0000:0b:00.0-usb-0:4:1.0-scsi-0:0:0:0 usb2
"
)
build_drive_map() { # Create box drawing elements
local host=$(hostname) h_line=$(printf '%*s' "$box_width" '' | tr ' ' '─')
declare -A drive_map
echo "DEBUG: Current host: $host" # USB Section (if applicable)
echo "DEBUG: Mapping found: ${DRIVE_MAPPINGS[$host]}" if [[ -n "${DRIVE_MAP[usb1]}" || -n "${DRIVE_MAP[usb2]}" ]]; then
printf "\n External USB\n"
local mapping=${DRIVE_MAPPINGS[$host]} printf " ┌%s┐ ┌%s┐\n" "$h_line" "$h_line"
printf " │ %-${max_width}s │ │ %-${max_width}s │\n" "${DRIVE_MAP[usb1]:-EMPTY}" "${DRIVE_MAP[usb2]:-EMPTY}"
if [[ -n "$mapping" ]]; then printf " └%s┘ └%s┘\n\n" "$h_line" "$h_line"
while read -r path slot; do
[[ -z "$path" || -z "$slot" ]] && continue
echo "DEBUG: Checking path: $path for slot: $slot"
if [[ -L "/dev/disk/by-path/$path" ]]; then
local drive=$(readlink -f "/dev/disk/by-path/$path" | sed 's/.*\///')
drive_map[$slot]=$drive
echo "DEBUG: Mapped slot $slot to drive $drive"
fi
done <<< "$mapping"
fi fi
# Make drive_map available globally # Main chassis section
declare -g -A DRIVE_MAP=() printf "┌──────────────────────────────────────────────────────────────┐\n"
for key in "${!drive_map[@]}"; do printf "│ %-58s │\n" "$hostname"
DRIVE_MAP[$key]=${drive_map[$key]} printf "│ %-58s │\n" "10-Bay Hot-swap Chassis"
echo "DEBUG: Final mapping - slot $key: ${drive_map[$key]}" 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 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"
} }
# Define the ASCII art maps generate_large1_layout() {
large1=''' build_drive_map
cat << 'EOF'
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ large1 │ │ large1 │
│ Unique 3x5 Grid Chassis │
│ │ │ │
│ ┌──────────────────────────────────────────────┐ │ │ ┌──────────────────────────────────────────────┐ │
│ │ Motherboard │ │ │ │ Motherboard │ │
@@ -104,10 +105,14 @@ large1='''
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
''' EOF
}
compute-storage-gpu-01=''' generate_spare_10bay_layout() {
cat << 'EOF'
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ Spare 10-Bay Chassis │
│ (Not Currently Deployed) │
│ │ │ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ │ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │
@@ -117,201 +122,175 @@ compute-storage-gpu-01='''
│ │ 5 │ │ 6 │ │ 7 │ │ 8 │ │ │ │ 5 │ │ 6 │ │ 7 │ │ 8 │ │
│ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │
│ │ │ │
│ │
│ │
│ ┌─────────┐ │ │ ┌─────────┐ │
compute-storage-gpu-01 │ 9 │ │ │ 9 │ │
│ └─────────┘ │ │ └─────────┘ │
│ ┌─────────┐ │ │ ┌─────────┐ │
│ │ 10 │ │ │ │ 10 │ │
│ └─────────┘ │ │ └─────────┘ │
│ │ │ │
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘
''' EOF
generate_medium2_layout() {
build_drive_map
# Calculate max width needed for drive names
max_width=0
for bay in {1..10} "11" "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
printf "\n External USB [0b:00.0]\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"
# Main chassis section
printf "┌──────────────────────────────────────────────────────────────┐\n"
printf "│ B650D4U3-2Q/BCM │\n"
printf "│ │\n"
printf "│ NVMe [0d:00.0] Bay 11 │\n"
printf "│ ┌%s┐ │\n" "$h_line"
printf "│ │ %-${max_width}s │ │\n" "${DRIVE_MAP[11]:-EMPTY}"
printf "│ └%s┘ │\n" "$h_line"
printf "│ │\n"
printf "│ Front Hot-swap Bays [0c:00.0] │\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"
} }
microGeneric=''' #------------------------------------------------------------------------------
┌─┐ ┌─┐ # Server-Specific Drive Mappings
┌└─┘──└─┘┐ # Maps PCI paths to physical bay numbers for each server
│ 1 2 │ # Format: "pci-path bay-number"
│ │ #------------------------------------------------------------------------------
│ │
│ │ declare -A SERVER_MAPPINGS=(
# compute-storage-01 (formerly medium2)
# Motherboard: B650D4U3-2Q/BCM
# Controller at 0c:00.0 for hot-swap bays
# Controller at 0d:00.0 for M.2 NVMe
└────────┘ ["compute-storage-01"]="
''' pci-0000:0c:00.0-ata-3 5
pci-0000:0c:00.0-ata-4 6
pci-0000:0c:00.0-ata-1 3
pci-0000:0c:00.0-ata-2 4
pci-0000:0d:00.0-nvme-1 m2-1
pci-0000:0b:00.0-usb-0:3:1.0-scsi-0:0:0:0 usb1
pci-0000:0b:00.0-usb-0:4:1.0-scsi-0:0:0:0 usb2
"
# 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"]="spare-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
#------------------------------------------------------------------------------
# Get the hostname
HOSTNAME=$(hostname) HOSTNAME=$(hostname)
CHASSIS_TYPE=${CHASSIS_TYPES[$HOSTNAME]:-"unknown"}
# ASCII art based on hostname # Display chassis layout
case "$HOSTNAME" in case "$CHASSIS_TYPE" in
"10bay")
generate_10bay_layout "$HOSTNAME"
;;
"large1") "large1")
echo -e "$large1" generate_large1_layout
;; ;;
"compute-storage-gpu-01") "spare-10bay")
echo -e "$compute-storage-gpu-01" generate_spare_10bay_layout
;; ;;
"medium2") "micro")
generate_medium2_layout echo "Micro server layout not yet implemented"
;;
"micro1" | "monitor-02")
echo -e "$microGeneric"
;; ;;
*) *)
echo -e "No ASCII map defined for this hostname." echo "┌─────────────────────────────────────────────────────────┐"
echo "│ Unknown server: $HOSTNAME"
echo "│ No chassis mapping defined yet"
echo "│ Run diagnose-drives.sh to gather PCI path information"
echo "└─────────────────────────────────────────────────────────┘"
;; ;;
esac esac
map_drives_to_layout() { #------------------------------------------------------------------------------
local server_type=$1 # Drive Details Section
case $server_type in #------------------------------------------------------------------------------
"large1")
for i in {1..9}; do
local drive_info=$(get_drive_info_for_position $i)
echo "Position $i: $drive_info"
done
;;
esac
}
DRIVE_PATHS=$(get_drives_info | awk '{print $1, $2}') echo -e "\n=== Drive Details with SMART Status ==="
printf "%-15s %-10s %-8s %-8s %-8s %-30s\n" "DEVICE" "SIZE" "TYPE" "TEMP" "HEALTH" "MODEL"
# Initialize array for "not found" messages
not_found=()
echo -e "\n=== Drive Details with SMART Status ===\n"
printf "%-15s %-10s %-8s %-8s %-20s %-30s\n" "DEVICE" "SIZE" "TYPE" "TEMP" "HEALTH" "MODEL"
echo "--------------------------------------------------------------------------------" echo "--------------------------------------------------------------------------------"
# For SATA drives # SATA/SAS drives
lsblk -d -o NAME | grep -v "nvme" | grep -v "rbd" | while read device; do lsblk -d -o NAME | grep -v "nvme" | grep -v "rbd" | grep -v "loop" | grep -v "NAME" | while read device; do
size=$(get_drive_details "$device") if [ -b "/dev/$device" ]; then
smart_info=$(get_drive_smart_info "$device") size=$(lsblk -d -n -o SIZE "/dev/$device" 2>/dev/null)
IFS='|' read -r type temp health model <<< "$smart_info"
printf "%-15s %-10s %-8s %-8s %-20s %-30s\n" "/dev/$device" "$size" "$type" "$temp" "$health" "$model"
done
# For NVMe drives
if [ -n "$nvme_drives" ]; then
while read -r line; do
device=$(echo "$line" | awk '{print $1}' | sed 's/.*\///')
size=$(echo "$line" | awk '{print $6}')
smart_info=$(get_drive_smart_info "$device") smart_info=$(get_drive_smart_info "$device")
IFS='|' read -r type temp health model <<< "$smart_info" IFS='|' read -r type temp health model <<< "$smart_info"
printf "%-15s %-10s %-8s %-8s %-20s %-30s\n" "/dev/$device" "$size" "$type" "$temp" "$health" "$model" printf "%-15s %-10s %-8s %-8s %-8s %-30s\n" "/dev/$device" "$size" "$type" "$temp" "$health" "$model"
done <<< "$nvme_drives" 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 fi
# Show NVMe Drives only if present #------------------------------------------------------------------------------
nvme_drives=$(sudo nvme list | grep "^/dev") # Optional sections
if [ -n "$nvme_drives" ]; then #------------------------------------------------------------------------------
echo -e "\n=== NVMe Drives ===\n"
printf "%-15s %-10s %-10s %-20s\n" "DEVICE" "SIZE" "TYPE" "MODEL"
echo "------------------------------------------------------------"
echo "$nvme_drives" | awk '{printf "%-15s %-10s %-10s %-20s\n", $1, $6, "NVMe", $3}'
else
not_found+=("NVMe drives")
fi
# Show MMC Drives only if present # Ceph RBD Devices
mmc_output=$(lsblk -o NAME,SIZE,TYPE,MOUNTPOINT | grep "mmcblk" | sort) rbd_output=$(lsblk -o NAME,SIZE,TYPE,MOUNTPOINT 2>/dev/null | grep "rbd" | sort -V)
if [ -n "$mmc_output" ]; then
echo -e "\n=== MMC Drives ===\n"
printf "%-15s %-10s %-10s %-20s\n" "DEVICE" "SIZE" "TYPE" "MOUNTPOINT"
echo "------------------------------------------------------------"
echo "$mmc_output"
fi
# Show SATA Drives only if present
sata_output=$(lsblk -d -o NAME,SIZE,TYPE,MOUNTPOINT | grep "disk" | grep -v "nvme" | grep -v "rbd" | sort | column -t)s
if [ -n "$sata_output" ]; then
echo -e "\n=== SATA Drives ===\n"
printf "%-15s %-10s %-10s %-20s\n" "DEVICE" "SIZE" "TYPE" "MOUNTPOINT"
echo "------------------------------------------------------------"
echo "$sata_output"
fi
# Show Ceph RBD Devices only if present
rbd_output=$(lsblk -o NAME,SIZE,TYPE,MOUNTPOINT | grep "rbd" | sort -V)
if [ -n "$rbd_output" ]; then if [ -n "$rbd_output" ]; then
echo -e "\n=== Ceph RBD Devices ===\n" echo -e "\n=== Ceph RBD Devices ==="
printf "%-15s %-10s %-10s %-20s\n" "DEVICE" "SIZE" "TYPE" "MOUNTPOINT" printf "%-15s %-10s %-10s %-20s\n" "DEVICE" "SIZE" "TYPE" "MOUNTPOINT"
echo "------------------------------------------------------------" echo "------------------------------------------------------------"
echo "$rbd_output" echo "$rbd_output"
else
not_found+=("RBD devices")
fi fi
# Check RAID # Show mapping diagnostic info if DEBUG is set
if ! [ -f /proc/mdstat ] || ! grep -q "active" /proc/mdstat; then if [[ -n "$DEBUG" ]]; then
not_found+=("Software RAID") echo -e "\n=== DEBUG: Drive Mappings ==="
fi for key in "${!DRIVE_MAP[@]}"; do
echo "Bay $key: ${DRIVE_MAP[$key]}"
# Check ZFS done | sort -n
if ! command -v zpool >/dev/null 2>&1 || [ -z "$(sudo zpool status 2>/dev/null)" ]; then
not_found+=("ZFS pools")
fi
# Display consolidated "not found" messages at the end
if [ ${#not_found[@]} -gt 0 ]; then
echo -e "\n=== Not Found ===\n"
printf "%s\n" "${not_found[@]}"
fi fi