diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index ea16bfe..56bf2a0 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -264,7 +264,7 @@ function TableView({ enclosure, onSelect, selectedSerial, t }) {
- {["Slot", "Device", "Model", "Serial", "WWN", "FW", "Capacity", "Pool", "Temp", "Hours", "Health"].map((h) => (
+ {["Slot", "Device", "Model", "Serial", "WWN", "FW", "Capacity", "Pool", "Vdev", "Temp", "Hours", "Health"].map((h) => (
| {h} |
))}
@@ -304,6 +304,16 @@ function TableView({ enclosure, onSelect, selectedSerial, t }) {
{"\u2014"}
)}
+
+ {d.zfs_vdev ? (
+ {d.zfs_vdev}
+ ) : (
+ {"\u2014"}
+ )}
+ |
= 40 ? t.health.warning.text : t.text }}>{d.temperature_c != null ? `${d.temperature_c}\u00B0C` : "\u2014"} |
{formatHours(d.power_on_hours)} |
|
@@ -413,6 +423,14 @@ function DriveDetail({ slot, onClose, t }) {
}}>
{d.zfs_pool}
+ {d.zfs_vdev && (
+
+ / {d.zfs_vdev}
+
+ )}
dict[str, str]:
- """Return a dict mapping device names to ZFS pool names.
+async def get_zfs_pool_map() -> dict[str, dict]:
+ """Return a dict mapping device names to ZFS pool and vdev info.
- e.g. {"sda": "tank", "sdb": "tank", "sdc": "fast"}
+ e.g. {"sda": {"pool": "tank", "vdev": "raidz2-0"},
+ "sdb": {"pool": "fast", "vdev": "mirror-0"}}
"""
pool_map = {}
try:
@@ -33,19 +35,49 @@ async def get_zfs_pool_map() -> dict[str, str]:
return pool_map
current_pool = None
+ current_vdev = None
+ in_config = False
+
for line in stdout.decode().splitlines():
stripped = line.strip()
+
if stripped.startswith("pool:"):
current_pool = stripped.split(":", 1)[1].strip()
- elif current_pool and "/dev/" in stripped:
+ current_vdev = None
+ in_config = False
+ continue
+
+ if stripped.startswith("NAME") and "STATE" in stripped:
+ in_config = True
+ continue
+
+ if stripped.startswith("errors:") or stripped == "":
+ if stripped.startswith("errors:"):
+ in_config = False
+ continue
+
+ if not in_config or not current_pool:
+ continue
+
+ # Count leading tab depth to distinguish pool/vdev/device lines.
+ # Pool name = 1 tab, vdev = 2 tabs, device = 3+ tabs
+ tab_count = len(line) - len(line.lstrip('\t'))
+
+ if tab_count == 2 and "/dev/" not in stripped:
+ # This is a vdev line (mirror-0, raidz2-0, etc.)
+ current_vdev = stripped.split()[0]
+ elif "/dev/" in stripped:
parts = stripped.split()
dev_path = parts[0]
try:
real = os.path.realpath(dev_path)
dev_name = os.path.basename(real)
# Strip partition numbers (sda1 -> sda)
- dev_name = dev_name.rstrip("0123456789")
- pool_map[dev_name] = current_pool
+ dev_name = re.sub(r'\d+$', '', dev_name)
+ pool_map[dev_name] = {
+ "pool": current_pool,
+ "vdev": current_vdev or current_pool,
+ }
except Exception:
pass
except FileNotFoundError: