50%
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
package httpapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.avt.data-center.id/othman.suseno/atlas/internal/errors"
|
||||
"gitea.avt.data-center.id/othman.suseno/atlas/internal/models"
|
||||
)
|
||||
|
||||
// methodHandler routes requests based on HTTP method
|
||||
@@ -85,8 +88,9 @@ func (a *App) handlePoolOps(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if strings.HasSuffix(r.URL.Path, "/scrub") {
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
if r.Method == http.MethodPost {
|
||||
a.handleScrubPool(w, r)
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleScrubPool)).ServeHTTP(w, r)
|
||||
} else if r.Method == http.MethodGet {
|
||||
a.handleGetScrubStatus(w, r)
|
||||
} else {
|
||||
@@ -96,8 +100,9 @@ func (a *App) handlePoolOps(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if strings.HasSuffix(r.URL.Path, "/export") {
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
if r.Method == http.MethodPost {
|
||||
a.handleExportPool(w, r)
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleExportPool)).ServeHTTP(w, r)
|
||||
} else {
|
||||
writeError(w, errors.NewAPIError(errors.ErrCodeBadRequest, "method not allowed", http.StatusMethodNotAllowed))
|
||||
}
|
||||
@@ -106,50 +111,67 @@ func (a *App) handlePoolOps(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if strings.HasSuffix(r.URL.Path, "/spare") {
|
||||
if r.Method == http.MethodPost {
|
||||
a.handleAddSpareDisk(w, r)
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleAddSpareDisk)).ServeHTTP(w, r)
|
||||
} else {
|
||||
writeError(w, errors.NewAPIError(errors.ErrCodeBadRequest, "method not allowed", http.StatusMethodNotAllowed))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleGetPool(w, r) },
|
||||
nil,
|
||||
nil,
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleDeletePool(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleDeletePool)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
)(w, r)
|
||||
}
|
||||
|
||||
// handleDatasetOps routes dataset operations by method
|
||||
func (a *App) handleDatasetOps(w http.ResponseWriter, r *http.Request) {
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleGetDataset(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateDataset(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleUpdateDataset(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleDeleteDataset(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateDataset)).ServeHTTP(w, r)
|
||||
},
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleUpdateDataset)).ServeHTTP(w, r)
|
||||
},
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleDeleteDataset)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
)(w, r)
|
||||
}
|
||||
|
||||
// handleZVOLOps routes ZVOL operations by method
|
||||
func (a *App) handleZVOLOps(w http.ResponseWriter, r *http.Request) {
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleGetZVOL(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateZVOL(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateZVOL)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleDeleteZVOL(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleDeleteZVOL)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
)(w, r)
|
||||
}
|
||||
|
||||
// handleSnapshotOps routes snapshot operations by method
|
||||
func (a *App) handleSnapshotOps(w http.ResponseWriter, r *http.Request) {
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
// Check if it's a restore operation
|
||||
if strings.HasSuffix(r.URL.Path, "/restore") {
|
||||
if r.Method == http.MethodPost {
|
||||
a.handleRestoreSnapshot(w, r)
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleRestoreSnapshot)).ServeHTTP(w, r)
|
||||
} else {
|
||||
writeError(w, errors.ErrBadRequest("method not allowed"))
|
||||
}
|
||||
@@ -158,42 +180,67 @@ func (a *App) handleSnapshotOps(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleGetSnapshot(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateSnapshot(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateSnapshot)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleDeleteSnapshot(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleDeleteSnapshot)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
)(w, r)
|
||||
}
|
||||
|
||||
// handleSnapshotPolicyOps routes snapshot policy operations by method
|
||||
func (a *App) handleSnapshotPolicyOps(w http.ResponseWriter, r *http.Request) {
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleGetSnapshotPolicy(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateSnapshotPolicy(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleUpdateSnapshotPolicy(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleDeleteSnapshotPolicy(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateSnapshotPolicy)).ServeHTTP(w, r)
|
||||
},
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleUpdateSnapshotPolicy)).ServeHTTP(w, r)
|
||||
},
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleDeleteSnapshotPolicy)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
)(w, r)
|
||||
}
|
||||
|
||||
// handleSMBShareOps routes SMB share operations by method
|
||||
func (a *App) handleSMBShareOps(w http.ResponseWriter, r *http.Request) {
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleGetSMBShare(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateSMBShare(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleUpdateSMBShare(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleDeleteSMBShare(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateSMBShare)).ServeHTTP(w, r)
|
||||
},
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleUpdateSMBShare)).ServeHTTP(w, r)
|
||||
},
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleDeleteSMBShare)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
)(w, r)
|
||||
}
|
||||
|
||||
// handleNFSExportOps routes NFS export operations by method
|
||||
func (a *App) handleNFSExportOps(w http.ResponseWriter, r *http.Request) {
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleGetNFSExport(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateNFSExport(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleUpdateNFSExport(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleDeleteNFSExport(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateNFSExport)).ServeHTTP(w, r)
|
||||
},
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleUpdateNFSExport)).ServeHTTP(w, r)
|
||||
},
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleDeleteNFSExport)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
)(w, r)
|
||||
}
|
||||
@@ -206,6 +253,7 @@ func (a *App) handleBackupOps(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
// Check if it's a verify request
|
||||
@@ -217,12 +265,12 @@ func (a *App) handleBackupOps(w http.ResponseWriter, r *http.Request) {
|
||||
case http.MethodPost:
|
||||
// Restore backup (POST /api/v1/backups/{id}/restore)
|
||||
if strings.HasSuffix(r.URL.Path, "/restore") {
|
||||
a.handleRestoreBackup(w, r)
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleRestoreBackup)).ServeHTTP(w, r)
|
||||
} else {
|
||||
writeError(w, errors.ErrBadRequest("invalid backup operation"))
|
||||
}
|
||||
case http.MethodDelete:
|
||||
a.handleDeleteBackup(w, r)
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleDeleteBackup)).ServeHTTP(w, r)
|
||||
default:
|
||||
writeError(w, errors.ErrBadRequest("method not allowed"))
|
||||
}
|
||||
@@ -244,9 +292,10 @@ func (a *App) handleISCSITargetOps(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
if strings.HasSuffix(r.URL.Path, "/luns") {
|
||||
if r.Method == http.MethodPost {
|
||||
a.handleAddLUN(w, r)
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleAddLUN)).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
writeError(w, errors.NewAPIError(errors.ErrCodeBadRequest, "method not allowed", http.StatusMethodNotAllowed))
|
||||
@@ -255,7 +304,7 @@ func (a *App) handleISCSITargetOps(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if strings.HasSuffix(r.URL.Path, "/luns/remove") {
|
||||
if r.Method == http.MethodPost {
|
||||
a.handleRemoveLUN(w, r)
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleRemoveLUN)).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
writeError(w, errors.NewAPIError(errors.ErrCodeBadRequest, "method not allowed", http.StatusMethodNotAllowed))
|
||||
@@ -265,8 +314,12 @@ func (a *App) handleISCSITargetOps(w http.ResponseWriter, r *http.Request) {
|
||||
methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleGetISCSITarget(w, r) },
|
||||
nil,
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleUpdateISCSITarget(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleDeleteISCSITarget(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleUpdateISCSITarget)).ServeHTTP(w, r)
|
||||
},
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleDeleteISCSITarget)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
)(w, r)
|
||||
}
|
||||
@@ -304,22 +357,68 @@ func (a *App) handleUserOps(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// handleVTLDriveOps routes VTL drive operations by method
|
||||
func (a *App) handleVTLDriveOps(w http.ResponseWriter, r *http.Request) {
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleGetVTLDrive(w, r) },
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleUpdateVTLDrive)).ServeHTTP(w, r)
|
||||
},
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleDeleteVTLDrive)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
)(w, r)
|
||||
}
|
||||
|
||||
// handleVTLTapeOps routes VTL tape operations by method
|
||||
func (a *App) handleVTLTapeOps(w http.ResponseWriter, r *http.Request) {
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleGetVTLTape(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateVTLTape(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateVTLTape)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleDeleteVTLTape(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleDeleteVTLTape)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
)(w, r)
|
||||
}
|
||||
|
||||
// handleMediaChangerOps routes media changer operations by method
|
||||
func (a *App) handleMediaChangerOps(w http.ResponseWriter, r *http.Request) {
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
// Get single changer by ID
|
||||
libraryIDStr := pathParam(r, "/api/v1/vtl/changers/")
|
||||
libraryID, err := strconv.Atoi(libraryIDStr)
|
||||
if err != nil || libraryID <= 0 {
|
||||
writeError(w, errors.ErrValidation("invalid library_id"))
|
||||
return
|
||||
}
|
||||
changers, err := a.vtlService.ListMediaChangers()
|
||||
if err != nil {
|
||||
writeError(w, errors.ErrInternal(fmt.Sprintf("failed to list changers: %v", err)))
|
||||
return
|
||||
}
|
||||
for _, changer := range changers {
|
||||
if changer.LibraryID == libraryID {
|
||||
writeJSON(w, http.StatusOK, changer)
|
||||
return
|
||||
}
|
||||
}
|
||||
writeError(w, errors.ErrNotFound(fmt.Sprintf("media changer %d not found", libraryID)))
|
||||
},
|
||||
nil,
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleUpdateMediaChanger)).ServeHTTP(w, r)
|
||||
},
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleDeleteMediaChanger)).ServeHTTP(w, r)
|
||||
},
|
||||
nil,
|
||||
)(w, r)
|
||||
}
|
||||
|
||||
@@ -65,9 +65,14 @@ func (a *App) routes() {
|
||||
a.mux.HandleFunc("/api/openapi.yaml", a.handleOpenAPISpec)
|
||||
|
||||
// Backup & Restore
|
||||
// Define allowed roles for storage operations (Administrator and Operator, not Viewer)
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
|
||||
a.mux.HandleFunc("/api/v1/backups", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListBackups(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateBackup(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateBackup)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/backups/", a.handleBackupOps)
|
||||
@@ -85,7 +90,9 @@ func (a *App) routes() {
|
||||
))
|
||||
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) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreatePool)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/pools/available", methodHandler(
|
||||
@@ -94,21 +101,27 @@ func (a *App) routes() {
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/pools/import", methodHandler(
|
||||
nil,
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleImportPool(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleImportPool)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/pools/", a.handlePoolOps)
|
||||
|
||||
a.mux.HandleFunc("/api/v1/datasets", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListDatasets(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateDataset(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateDataset)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/datasets/", a.handleDatasetOps)
|
||||
|
||||
a.mux.HandleFunc("/api/v1/zvols", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListZVOLs(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateZVOL(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateZVOL)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/zvols/", a.handleZVOLOps)
|
||||
@@ -116,13 +129,17 @@ func (a *App) routes() {
|
||||
// Snapshot Management
|
||||
a.mux.HandleFunc("/api/v1/snapshots", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListSnapshots(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateSnapshot(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateSnapshot)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/snapshots/", a.handleSnapshotOps)
|
||||
a.mux.HandleFunc("/api/v1/snapshot-policies", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListSnapshotPolicies(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateSnapshotPolicy(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateSnapshotPolicy)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/snapshot-policies/", a.handleSnapshotPolicyOps)
|
||||
@@ -130,7 +147,9 @@ func (a *App) routes() {
|
||||
// Storage Services - SMB
|
||||
a.mux.HandleFunc("/api/v1/shares/smb", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListSMBShares(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateSMBShare(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateSMBShare)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/shares/smb/", a.handleSMBShareOps)
|
||||
@@ -138,7 +157,9 @@ func (a *App) routes() {
|
||||
// Storage Services - NFS
|
||||
a.mux.HandleFunc("/api/v1/exports/nfs", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListNFSExports(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateNFSExport(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateNFSExport)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/exports/nfs/", a.handleNFSExportOps)
|
||||
@@ -146,7 +167,9 @@ func (a *App) routes() {
|
||||
// Storage Services - iSCSI
|
||||
a.mux.HandleFunc("/api/v1/iscsi/targets", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListISCSITargets(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateISCSITarget(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateISCSITarget)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/iscsi/targets/", a.handleISCSITargetOps)
|
||||
@@ -158,24 +181,36 @@ func (a *App) routes() {
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/vtl/drives", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListVTLDrives(w, r) },
|
||||
nil, nil, nil, nil,
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
storageRoles := []models.Role{models.RoleAdministrator, models.RoleOperator}
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateVTLDrive)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/vtl/drives/", a.handleVTLDriveOps)
|
||||
a.mux.HandleFunc("/api/v1/vtl/tapes", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListVTLTapes(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleCreateVTLTape(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateVTLTape)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/vtl/tapes/", a.handleVTLTapeOps)
|
||||
a.mux.HandleFunc("/api/v1/vtl/service", methodHandler(
|
||||
nil,
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleVTLServiceControl(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleVTLServiceControl)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/vtl/changers", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListVTLMediaChangers(w, r) },
|
||||
nil, nil, nil, nil,
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleCreateMediaChanger)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/vtl/changers/", a.handleMediaChangerOps)
|
||||
a.mux.HandleFunc("/api/v1/vtl/devices/iscsi", methodHandler(
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleListVTLDevicesForISCSI(w, r) },
|
||||
nil, nil, nil, nil,
|
||||
@@ -186,12 +221,16 @@ func (a *App) routes() {
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/vtl/tape/load", methodHandler(
|
||||
nil,
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleLoadTape(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleLoadTape)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
a.mux.HandleFunc("/api/v1/vtl/tape/eject", methodHandler(
|
||||
nil,
|
||||
func(w http.ResponseWriter, r *http.Request) { a.handleEjectTape(w, r) },
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
a.requireRole(storageRoles...)(http.HandlerFunc(a.handleEjectTape)).ServeHTTP(w, r)
|
||||
},
|
||||
nil, nil, nil,
|
||||
))
|
||||
|
||||
|
||||
@@ -286,6 +286,193 @@ func (a *App) handleEjectTape(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// handleCreateMediaChanger creates a new media changer/library
|
||||
func (a *App) handleCreateMediaChanger(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
LibraryID int `json:"library_id"`
|
||||
Vendor string `json:"vendor"`
|
||||
Product string `json:"product"`
|
||||
Serial string `json:"serial"`
|
||||
NumSlots int `json:"num_slots"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, errors.ErrValidation(fmt.Sprintf("invalid request body: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
if req.LibraryID <= 0 {
|
||||
writeError(w, errors.ErrValidation("library_id must be greater than 0"))
|
||||
return
|
||||
}
|
||||
|
||||
if req.NumSlots <= 0 {
|
||||
req.NumSlots = 10 // Default number of slots
|
||||
}
|
||||
|
||||
if err := a.vtlService.AddMediaChanger(req.LibraryID, req.Vendor, req.Product, req.Serial, req.NumSlots); err != nil {
|
||||
log.Printf("create media changer error: %v", err)
|
||||
writeError(w, errors.ErrInternal(fmt.Sprintf("failed to create media changer: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusCreated, map[string]interface{}{
|
||||
"message": "Media changer created successfully",
|
||||
"library_id": req.LibraryID,
|
||||
})
|
||||
}
|
||||
|
||||
// handleUpdateMediaChanger updates a media changer/library configuration
|
||||
func (a *App) handleUpdateMediaChanger(w http.ResponseWriter, r *http.Request) {
|
||||
libraryIDStr := pathParam(r, "/api/v1/vtl/changers/")
|
||||
libraryID, err := strconv.Atoi(libraryIDStr)
|
||||
if err != nil || libraryID <= 0 {
|
||||
writeError(w, errors.ErrValidation("invalid library_id"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Vendor string `json:"vendor"`
|
||||
Product string `json:"product"`
|
||||
Serial string `json:"serial"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, errors.ErrValidation(fmt.Sprintf("invalid request body: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.vtlService.UpdateMediaChanger(libraryID, req.Vendor, req.Product, req.Serial); err != nil {
|
||||
log.Printf("update media changer error: %v", err)
|
||||
writeError(w, errors.ErrInternal(fmt.Sprintf("failed to update media changer: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"message": "Media changer updated successfully",
|
||||
"library_id": libraryID,
|
||||
})
|
||||
}
|
||||
|
||||
// handleDeleteMediaChanger removes a media changer/library
|
||||
func (a *App) handleDeleteMediaChanger(w http.ResponseWriter, r *http.Request) {
|
||||
libraryIDStr := pathParam(r, "/api/v1/vtl/changers/")
|
||||
libraryID, err := strconv.Atoi(libraryIDStr)
|
||||
if err != nil || libraryID <= 0 {
|
||||
writeError(w, errors.ErrValidation("invalid library_id"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.vtlService.RemoveMediaChanger(libraryID); err != nil {
|
||||
log.Printf("delete media changer error: %v", err)
|
||||
writeError(w, errors.ErrInternal(fmt.Sprintf("failed to delete media changer: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"message": "Media changer deleted successfully",
|
||||
"library_id": libraryID,
|
||||
})
|
||||
}
|
||||
|
||||
// handleCreateVTLDrive creates a new drive
|
||||
func (a *App) handleCreateVTLDrive(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
DriveID int `json:"drive_id"`
|
||||
LibraryID int `json:"library_id"`
|
||||
SlotID int `json:"slot_id"`
|
||||
Vendor string `json:"vendor"`
|
||||
Product string `json:"product"`
|
||||
Serial string `json:"serial"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, errors.ErrValidation(fmt.Sprintf("invalid request body: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
if req.DriveID <= 0 {
|
||||
writeError(w, errors.ErrValidation("drive_id must be greater than 0"))
|
||||
return
|
||||
}
|
||||
|
||||
if req.LibraryID <= 0 {
|
||||
writeError(w, errors.ErrValidation("library_id must be greater than 0"))
|
||||
return
|
||||
}
|
||||
|
||||
if req.SlotID <= 0 {
|
||||
writeError(w, errors.ErrValidation("slot_id must be greater than 0"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.vtlService.AddDrive(req.DriveID, req.LibraryID, req.SlotID, req.Vendor, req.Product, req.Serial); err != nil {
|
||||
log.Printf("create VTL drive error: %v", err)
|
||||
writeError(w, errors.ErrInternal(fmt.Sprintf("failed to create VTL drive: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusCreated, map[string]interface{}{
|
||||
"message": "Drive created successfully",
|
||||
"drive_id": req.DriveID,
|
||||
})
|
||||
}
|
||||
|
||||
// handleUpdateVTLDrive updates a drive configuration
|
||||
func (a *App) handleUpdateVTLDrive(w http.ResponseWriter, r *http.Request) {
|
||||
driveIDStr := pathParam(r, "id")
|
||||
driveID, err := strconv.Atoi(driveIDStr)
|
||||
if err != nil || driveID <= 0 {
|
||||
writeError(w, errors.ErrValidation("invalid drive_id"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
LibraryID int `json:"library_id"`
|
||||
SlotID int `json:"slot_id"`
|
||||
Vendor string `json:"vendor"`
|
||||
Product string `json:"product"`
|
||||
Serial string `json:"serial"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, errors.ErrValidation(fmt.Sprintf("invalid request body: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.vtlService.UpdateDrive(driveID, req.LibraryID, req.SlotID, req.Vendor, req.Product, req.Serial); err != nil {
|
||||
log.Printf("update VTL drive error: %v", err)
|
||||
writeError(w, errors.ErrInternal(fmt.Sprintf("failed to update VTL drive: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]string{
|
||||
"message": "Drive updated successfully",
|
||||
"drive_id": fmt.Sprintf("%d", driveID),
|
||||
})
|
||||
}
|
||||
|
||||
// handleDeleteVTLDrive removes a drive
|
||||
func (a *App) handleDeleteVTLDrive(w http.ResponseWriter, r *http.Request) {
|
||||
driveIDStr := pathParam(r, "id")
|
||||
driveID, err := strconv.Atoi(driveIDStr)
|
||||
if err != nil || driveID <= 0 {
|
||||
writeError(w, errors.ErrValidation("invalid drive_id"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.vtlService.RemoveDrive(driveID); err != nil {
|
||||
log.Printf("delete VTL drive error: %v", err)
|
||||
writeError(w, errors.ErrInternal(fmt.Sprintf("failed to delete VTL drive: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]string{
|
||||
"message": "Drive deleted successfully",
|
||||
"drive_id": fmt.Sprintf("%d", driveID),
|
||||
})
|
||||
}
|
||||
|
||||
// handleListVTLDevicesForISCSI returns all tape devices (drives and medium changers) for iSCSI passthrough
|
||||
func (a *App) handleListVTLDevicesForISCSI(w http.ResponseWriter, r *http.Request) {
|
||||
devices := []map[string]interface{}{}
|
||||
|
||||
Reference in New Issue
Block a user