diff --git a/backend/routers/pools.py b/backend/routers/pools.py
index f591523..f785413 100644
--- a/backend/routers/pools.py
+++ b/backend/routers/pools.py
@@ -93,6 +93,38 @@ async def scrub_pool(pool_name: str, current_user: str = Depends(get_current_use
raise HTTPException(status_code=500, detail=str(e))
+@router.post("/{pool_name}/clear")
+async def clear_pool_errors(pool_name: str, current_user: str = Depends(get_current_user)):
+ """
+ Clear error counters on pool
+ """
+ try:
+ stdout, stderr, rc = zfs_runner.run_command(["zpool", "clear", pool_name])
+ if rc != 0:
+ raise HTTPException(status_code=400, detail=stderr.strip() or "Failed to clear errors")
+ return {"status": "success", "message": f"Errors cleared on {pool_name}"}
+ except HTTPException:
+ raise
+ except Exception as e:
+ raise HTTPException(status_code=500, detail=str(e))
+
+
+@router.post("/{pool_name}/resilver")
+async def resilver_pool(pool_name: str, current_user: str = Depends(get_current_user)):
+ """
+ Start resilver on pool
+ """
+ try:
+ stdout, stderr, rc = zfs_runner.run_command(["zpool", "resilver", pool_name])
+ if rc != 0:
+ raise HTTPException(status_code=400, detail=stderr.strip() or "Failed to start resilver")
+ return {"status": "success", "message": f"Resilver started on {pool_name}"}
+ except HTTPException:
+ raise
+ except Exception as e:
+ raise HTTPException(status_code=500, detail=str(e))
+
+
@router.post("/clear-cache")
async def clear_cache(current_user: str = Depends(get_current_user)):
"""
diff --git a/frontend/app/zfs/page.tsx b/frontend/app/zfs/page.tsx
index fd7d744..89bc34d 100644
--- a/frontend/app/zfs/page.tsx
+++ b/frontend/app/zfs/page.tsx
@@ -79,7 +79,7 @@ function VdevTree({ vdevs, depth = 0 }: { vdevs: any[]; depth?: number }) {
{v.write ?? 0} |
{v.cksum ?? 0} |
{v.message || ""} |
- {v.product || ""} |
+ {v.product || ""} |
|
e.stopPropagation()}>
- |
@@ -696,6 +716,31 @@ export default function ZfsPage() {
+ {/* Pool Context Menu */}
+ {poolMenu && (
+ <>
+ setPoolMenu(null)} />
+
+ {[
+ { action: "scrub", label: "Scrub Storage Pool" },
+ { action: "resilver", label: "Resilver Storage Pool" },
+ { action: "clear", label: "Clear Storage Pool Errors" },
+ ].map(({ action, label }) => (
+ handlePoolAction(action, poolMenu.pool)}
+ >
+ {label}
+
+ ))}
+
+ >
+ )}
+
{/* Snapshot Context Menu */}
{snapContextMenu && (
<>
diff --git a/frontend/lib/api.ts b/frontend/lib/api.ts
index 5a97596..00f1daf 100644
--- a/frontend/lib/api.ts
+++ b/frontend/lib/api.ts
@@ -223,6 +223,16 @@ export class ZFSManagerAPI {
return response.data
}
+ async clearPoolErrors(poolName: string): Promise<{ status: string }> {
+ const response = await this.client.post(`/api/pools/${poolName}/clear`)
+ return response.data
+ }
+
+ async resilverPool(poolName: string): Promise<{ status: string }> {
+ const response = await this.client.post(`/api/pools/${poolName}/resilver`)
+ return response.data
+ }
+
// Datasets
async getDatasets(pool: string = "tank"): Promise
{
const response = await this.client.get("/api/datasets/", { params: { pool } })