Integrating ZFS
Some checks failed
CI / test-build (push) Failing after 59s

This commit is contained in:
2025-12-14 23:00:18 +07:00
parent a6da313dfc
commit 461edbc970
4 changed files with 622 additions and 22 deletions

View File

@@ -2,6 +2,7 @@ package httpapp
import (
"encoding/json"
"log"
"net/http"
"gitea.avt.data-center.id/othman.suseno/atlas/internal/models"
@@ -9,88 +10,279 @@ import (
// pathParam is now in router_helpers.go
// Disk Handlers
func (a *App) handleListDisks(w http.ResponseWriter, r *http.Request) {
disks, err := a.zfs.ListDisks()
if err != nil {
log.Printf("list disks error: %v", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, disks)
}
// ZFS Pool Handlers
func (a *App) handleListPools(w http.ResponseWriter, r *http.Request) {
// TODO: Implement ZFS pool listing
pools := []models.Pool{} // Stub
pools, err := a.zfs.ListPools()
if err != nil {
log.Printf("list pools error: %v", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, pools)
}
func (a *App) handleCreatePool(w http.ResponseWriter, r *http.Request) {
// TODO: Implement pool creation
writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented"})
var req struct {
Name string `json:"name"`
VDEVs []string `json:"vdevs"`
Options map[string]string `json:"options,omitempty"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"})
return
}
if req.Name == "" || len(req.VDEVs) == 0 {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "name and vdevs are required"})
return
}
if req.Options == nil {
req.Options = make(map[string]string)
}
if err := a.zfs.CreatePool(req.Name, req.VDEVs, req.Options); err != nil {
log.Printf("create pool error: %v", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
pool, err := a.zfs.GetPool(req.Name)
if err != nil {
writeJSON(w, http.StatusCreated, map[string]string{"message": "pool created", "name": req.Name})
return
}
writeJSON(w, http.StatusCreated, pool)
}
func (a *App) handleGetPool(w http.ResponseWriter, r *http.Request) {
name := pathParam(r, "/api/v1/pools/")
// TODO: Implement pool retrieval
writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "name": name})
if name == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "pool name required"})
return
}
pool, err := a.zfs.GetPool(name)
if err != nil {
writeJSON(w, http.StatusNotFound, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, pool)
}
func (a *App) handleDeletePool(w http.ResponseWriter, r *http.Request) {
name := pathParam(r, "/api/v1/pools/")
// TODO: Implement pool deletion
writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "name": name})
if name == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "pool name required"})
return
}
if err := a.zfs.DestroyPool(name); err != nil {
log.Printf("destroy pool error: %v", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, map[string]string{"message": "pool destroyed", "name": name})
}
func (a *App) handleScrubPool(w http.ResponseWriter, r *http.Request) {
name := pathParam(r, "/api/v1/pools/")
// TODO: Implement pool scrub
writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "name": name})
if name == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "pool name required"})
return
}
if err := a.zfs.ScrubPool(name); err != nil {
log.Printf("scrub pool error: %v", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, map[string]string{"message": "scrub started", "pool": name})
}
// Dataset Handlers
func (a *App) handleListDatasets(w http.ResponseWriter, r *http.Request) {
datasets := []models.Dataset{} // Stub
pool := r.URL.Query().Get("pool")
datasets, err := a.zfs.ListDatasets(pool)
if err != nil {
log.Printf("list datasets error: %v", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, datasets)
}
func (a *App) handleCreateDataset(w http.ResponseWriter, r *http.Request) {
var req struct {
Name string `json:"name"`
Pool string `json:"pool"`
Name string `json:"name"`
Options map[string]string `json:"options,omitempty"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request"})
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"})
return
}
// TODO: Implement dataset creation
writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented"})
if req.Name == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "dataset name is required"})
return
}
if req.Options == nil {
req.Options = make(map[string]string)
}
if err := a.zfs.CreateDataset(req.Name, req.Options); err != nil {
log.Printf("create dataset error: %v", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusCreated, map[string]string{"message": "dataset created", "name": req.Name})
}
func (a *App) handleGetDataset(w http.ResponseWriter, r *http.Request) {
name := pathParam(r, "/api/v1/datasets/")
writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "name": name})
if name == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "dataset name required"})
return
}
datasets, err := a.zfs.ListDatasets("")
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
for _, ds := range datasets {
if ds.Name == name {
writeJSON(w, http.StatusOK, ds)
return
}
}
writeJSON(w, http.StatusNotFound, map[string]string{"error": "dataset not found"})
}
func (a *App) handleUpdateDataset(w http.ResponseWriter, r *http.Request) {
name := pathParam(r, "/api/v1/datasets/")
// TODO: Implement dataset property updates
writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "name": name})
}
func (a *App) handleDeleteDataset(w http.ResponseWriter, r *http.Request) {
name := pathParam(r, "/api/v1/datasets/")
writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "name": name})
if name == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "dataset name required"})
return
}
recursive := r.URL.Query().Get("recursive") == "true"
if err := a.zfs.DestroyDataset(name, recursive); err != nil {
log.Printf("destroy dataset error: %v", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, map[string]string{"message": "dataset destroyed", "name": name})
}
// ZVOL Handlers
func (a *App) handleListZVOLs(w http.ResponseWriter, r *http.Request) {
zvols := []models.ZVOL{} // Stub
pool := r.URL.Query().Get("pool")
zvols, err := a.zfs.ListZVOLs(pool)
if err != nil {
log.Printf("list zvols error: %v", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, zvols)
}
func (a *App) handleCreateZVOL(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented"})
var req struct {
Name string `json:"name"`
Size uint64 `json:"size"` // in bytes
Options map[string]string `json:"options,omitempty"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"})
return
}
if req.Name == "" || req.Size == 0 {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "name and size are required"})
return
}
if req.Options == nil {
req.Options = make(map[string]string)
}
if err := a.zfs.CreateZVOL(req.Name, req.Size, req.Options); err != nil {
log.Printf("create zvol error: %v", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusCreated, map[string]string{"message": "zvol created", "name": req.Name})
}
func (a *App) handleGetZVOL(w http.ResponseWriter, r *http.Request) {
name := pathParam(r, "/api/v1/zvols/")
writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "name": name})
if name == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "zvol name required"})
return
}
zvols, err := a.zfs.ListZVOLs("")
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
for _, zvol := range zvols {
if zvol.Name == name {
writeJSON(w, http.StatusOK, zvol)
return
}
}
writeJSON(w, http.StatusNotFound, map[string]string{"error": "zvol not found"})
}
func (a *App) handleDeleteZVOL(w http.ResponseWriter, r *http.Request) {
name := pathParam(r, "/api/v1/zvols/")
writeJSON(w, http.StatusNotImplemented, map[string]string{"error": "not implemented", "name": name})
if name == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "zvol name required"})
return
}
if err := a.zfs.DestroyZVOL(name); err != nil {
log.Printf("destroy zvol error: %v", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, map[string]string{"message": "zvol destroyed", "name": name})
}
// Snapshot Handlers

View File

@@ -6,6 +6,8 @@ import (
"net/http"
"path/filepath"
"time"
"gitea.avt.data-center.id/othman.suseno/atlas/internal/zfs"
)
type Config struct {
@@ -18,6 +20,7 @@ type App struct {
cfg Config
tmpl *template.Template
mux *http.ServeMux
zfs *zfs.Service
}
func New(cfg Config) (*App, error) {
@@ -37,6 +40,7 @@ func New(cfg Config) (*App, error) {
cfg: cfg,
tmpl: tmpl,
mux: http.NewServeMux(),
zfs: zfs.New(),
}
a.routes()

View File

@@ -15,6 +15,10 @@ func (a *App) routes() {
a.mux.HandleFunc("/metrics", a.handleMetrics)
// API v1 routes - ZFS Management
a.mux.HandleFunc("/api/v1/disks", methodHandler(
func(w http.ResponseWriter, r *http.Request) { a.handleListDisks(w, r) },
nil, nil, nil, nil,
))
a.mux.HandleFunc("/api/v1/pools", methodHandler(
func(w http.ResponseWriter, r *http.Request) { a.handleListPools(w, r) },
func(w http.ResponseWriter, r *http.Request) { a.handleCreatePool(w, r) },