working on system management
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package tape_vtl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/atlasos/calypso/internal/common/database"
|
||||
@@ -29,6 +30,7 @@ func NewHandler(db *database.DB, log *logger.Logger) *Handler {
|
||||
|
||||
// ListLibraries lists all virtual tape libraries
|
||||
func (h *Handler) ListLibraries(c *gin.Context) {
|
||||
h.logger.Info("ListLibraries called")
|
||||
libraries, err := h.service.ListLibraries(c.Request.Context())
|
||||
if err != nil {
|
||||
h.logger.Error("Failed to list libraries", "error", err)
|
||||
@@ -36,7 +38,36 @@ func (h *Handler) ListLibraries(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"libraries": libraries})
|
||||
h.logger.Info("ListLibraries result", "count", len(libraries), "is_nil", libraries == nil)
|
||||
|
||||
// Ensure we return an empty array instead of null
|
||||
if libraries == nil {
|
||||
h.logger.Warn("Libraries is nil, converting to empty array")
|
||||
libraries = []VirtualTapeLibrary{}
|
||||
}
|
||||
|
||||
h.logger.Info("Returning libraries", "count", len(libraries), "libraries", libraries)
|
||||
|
||||
// Ensure we always return an array, never null
|
||||
if libraries == nil {
|
||||
libraries = []VirtualTapeLibrary{}
|
||||
}
|
||||
|
||||
// Force empty array if nil (double check)
|
||||
if libraries == nil {
|
||||
h.logger.Warn("Libraries is still nil in handler, forcing empty array")
|
||||
libraries = []VirtualTapeLibrary{}
|
||||
}
|
||||
|
||||
// Use explicit JSON marshalling to ensure empty array, not null
|
||||
response := map[string]interface{}{
|
||||
"libraries": libraries,
|
||||
}
|
||||
|
||||
h.logger.Info("Response payload", "count", len(libraries), "response_type", fmt.Sprintf("%T", libraries))
|
||||
|
||||
// Use JSON marshalling that handles empty slices correctly
|
||||
c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// GetLibrary retrieves a library by ID
|
||||
@@ -69,11 +100,11 @@ func (h *Handler) GetLibrary(c *gin.Context) {
|
||||
|
||||
// CreateLibraryRequest represents a library creation request
|
||||
type CreateLibraryRequest struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
BackingStorePath string `json:"backing_store_path" binding:"required"`
|
||||
SlotCount int `json:"slot_count" binding:"required"`
|
||||
DriveCount int `json:"drive_count" binding:"required"`
|
||||
SlotCount int `json:"slot_count" binding:"required"`
|
||||
DriveCount int `json:"drive_count" binding:"required"`
|
||||
}
|
||||
|
||||
// CreateLibrary creates a new virtual tape library
|
||||
@@ -161,10 +192,10 @@ func (h *Handler) GetLibraryTapes(c *gin.Context) {
|
||||
|
||||
// CreateTapeRequest represents a tape creation request
|
||||
type CreateTapeRequest struct {
|
||||
Barcode string `json:"barcode" binding:"required"`
|
||||
SlotNumber int `json:"slot_number" binding:"required"`
|
||||
TapeType string `json:"tape_type" binding:"required"`
|
||||
SizeGB int64 `json:"size_gb" binding:"required"`
|
||||
Barcode string `json:"barcode" binding:"required"`
|
||||
SlotNumber int `json:"slot_number" binding:"required"`
|
||||
TapeType string `json:"tape_type" binding:"required"`
|
||||
SizeGB int64 `json:"size_gb" binding:"required"`
|
||||
}
|
||||
|
||||
// CreateTape creates a new virtual tape
|
||||
@@ -218,9 +249,9 @@ func (h *Handler) LoadTape(c *gin.Context) {
|
||||
// Create async task
|
||||
taskID, err := h.taskEngine.CreateTask(c.Request.Context(),
|
||||
tasks.TaskTypeLoadUnload, userID.(string), map[string]interface{}{
|
||||
"operation": "load_tape",
|
||||
"library_id": libraryID,
|
||||
"slot_number": req.SlotNumber,
|
||||
"operation": "load_tape",
|
||||
"library_id": libraryID,
|
||||
"slot_number": req.SlotNumber,
|
||||
"drive_number": req.DriveNumber,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -268,9 +299,9 @@ func (h *Handler) UnloadTape(c *gin.Context) {
|
||||
// Create async task
|
||||
taskID, err := h.taskEngine.CreateTask(c.Request.Context(),
|
||||
tasks.TaskTypeLoadUnload, userID.(string), map[string]interface{}{
|
||||
"operation": "unload_tape",
|
||||
"library_id": libraryID,
|
||||
"slot_number": req.SlotNumber,
|
||||
"operation": "unload_tape",
|
||||
"library_id": libraryID,
|
||||
"slot_number": req.SlotNumber,
|
||||
"drive_number": req.DriveNumber,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -295,4 +326,3 @@ func (h *Handler) UnloadTape(c *gin.Context) {
|
||||
|
||||
c.JSON(http.StatusAccepted, gin.H{"task_id": taskID})
|
||||
}
|
||||
|
||||
|
||||
@@ -28,46 +28,46 @@ func NewService(db *database.DB, log *logger.Logger) *Service {
|
||||
|
||||
// VirtualTapeLibrary represents a virtual tape library
|
||||
type VirtualTapeLibrary struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
MHVTLibraryID int `json:"mhvtl_library_id"`
|
||||
BackingStorePath string `json:"backing_store_path"`
|
||||
SlotCount int `json:"slot_count"`
|
||||
DriveCount int `json:"drive_count"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
MHVTLibraryID int `json:"mhvtl_library_id"`
|
||||
BackingStorePath string `json:"backing_store_path"`
|
||||
SlotCount int `json:"slot_count"`
|
||||
DriveCount int `json:"drive_count"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
}
|
||||
|
||||
// VirtualTapeDrive represents a virtual tape drive
|
||||
type VirtualTapeDrive struct {
|
||||
ID string `json:"id"`
|
||||
LibraryID string `json:"library_id"`
|
||||
DriveNumber int `json:"drive_number"`
|
||||
DevicePath *string `json:"device_path,omitempty"`
|
||||
StablePath *string `json:"stable_path,omitempty"`
|
||||
Status string `json:"status"`
|
||||
CurrentTapeID string `json:"current_tape_id,omitempty"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ID string `json:"id"`
|
||||
LibraryID string `json:"library_id"`
|
||||
DriveNumber int `json:"drive_number"`
|
||||
DevicePath *string `json:"device_path,omitempty"`
|
||||
StablePath *string `json:"stable_path,omitempty"`
|
||||
Status string `json:"status"`
|
||||
CurrentTapeID string `json:"current_tape_id,omitempty"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// VirtualTape represents a virtual tape
|
||||
type VirtualTape struct {
|
||||
ID string `json:"id"`
|
||||
LibraryID string `json:"library_id"`
|
||||
Barcode string `json:"barcode"`
|
||||
SlotNumber int `json:"slot_number"`
|
||||
ImageFilePath string `json:"image_file_path"`
|
||||
SizeBytes int64 `json:"size_bytes"`
|
||||
UsedBytes int64 `json:"used_bytes"`
|
||||
TapeType string `json:"tape_type"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ID string `json:"id"`
|
||||
LibraryID string `json:"library_id"`
|
||||
Barcode string `json:"barcode"`
|
||||
SlotNumber int `json:"slot_number"`
|
||||
ImageFilePath string `json:"image_file_path"`
|
||||
SizeBytes int64 `json:"size_bytes"`
|
||||
UsedBytes int64 `json:"used_bytes"`
|
||||
TapeType string `json:"tape_type"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// CreateLibrary creates a new virtual tape library
|
||||
@@ -135,14 +135,14 @@ func (s *Service) CreateLibrary(ctx context.Context, name, description, backingS
|
||||
for i := 1; i <= slotCount; i++ {
|
||||
barcode := fmt.Sprintf("V%05d", i)
|
||||
tape := VirtualTape{
|
||||
LibraryID: lib.ID,
|
||||
Barcode: barcode,
|
||||
SlotNumber: i,
|
||||
LibraryID: lib.ID,
|
||||
Barcode: barcode,
|
||||
SlotNumber: i,
|
||||
ImageFilePath: filepath.Join(tapesPath, fmt.Sprintf("%s.img", barcode)),
|
||||
SizeBytes: 800 * 1024 * 1024 * 1024, // 800 GB default (LTO-8)
|
||||
UsedBytes: 0,
|
||||
TapeType: "LTO-8",
|
||||
Status: "idle",
|
||||
SizeBytes: 800 * 1024 * 1024 * 1024, // 800 GB default (LTO-8)
|
||||
UsedBytes: 0,
|
||||
TapeType: "LTO-8",
|
||||
Status: "idle",
|
||||
}
|
||||
if err := s.createTape(ctx, &tape); err != nil {
|
||||
s.logger.Error("Failed to create tape", "slot", i, "error", err)
|
||||
@@ -228,28 +228,56 @@ func (s *Service) ListLibraries(ctx context.Context) ([]VirtualTapeLibrary, erro
|
||||
ORDER BY name
|
||||
`
|
||||
|
||||
s.logger.Info("Executing query to list libraries")
|
||||
rows, err := s.db.QueryContext(ctx, query)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to query libraries", "error", err)
|
||||
return nil, fmt.Errorf("failed to list libraries: %w", err)
|
||||
}
|
||||
s.logger.Info("Query executed successfully, got rows")
|
||||
defer rows.Close()
|
||||
|
||||
var libraries []VirtualTapeLibrary
|
||||
libraries := make([]VirtualTapeLibrary, 0) // Initialize as empty slice, not nil
|
||||
s.logger.Info("Starting to scan library rows", "query", query)
|
||||
rowCount := 0
|
||||
for rows.Next() {
|
||||
rowCount++
|
||||
var lib VirtualTapeLibrary
|
||||
var description sql.NullString
|
||||
var createdBy sql.NullString
|
||||
err := rows.Scan(
|
||||
&lib.ID, &lib.Name, &lib.Description, &lib.MHVTLibraryID, &lib.BackingStorePath,
|
||||
&lib.ID, &lib.Name, &description, &lib.MHVTLibraryID, &lib.BackingStorePath,
|
||||
&lib.SlotCount, &lib.DriveCount, &lib.IsActive,
|
||||
&lib.CreatedAt, &lib.UpdatedAt, &lib.CreatedBy,
|
||||
&lib.CreatedAt, &lib.UpdatedAt, &createdBy,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to scan library", "error", err)
|
||||
s.logger.Error("Failed to scan library", "error", err, "row", rowCount)
|
||||
continue
|
||||
}
|
||||
if description.Valid {
|
||||
lib.Description = description.String
|
||||
}
|
||||
if createdBy.Valid {
|
||||
lib.CreatedBy = createdBy.String
|
||||
}
|
||||
libraries = append(libraries, lib)
|
||||
s.logger.Info("Added library to list", "library_id", lib.ID, "name", lib.Name, "mhvtl_id", lib.MHVTLibraryID)
|
||||
}
|
||||
s.logger.Info("Finished scanning library rows", "total_rows", rowCount, "libraries_added", len(libraries))
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
s.logger.Error("Error iterating library rows", "error", err)
|
||||
return nil, fmt.Errorf("error iterating library rows: %w", err)
|
||||
}
|
||||
|
||||
return libraries, rows.Err()
|
||||
s.logger.Info("Listed virtual tape libraries", "count", len(libraries), "is_nil", libraries == nil)
|
||||
// Ensure we return an empty slice, not nil
|
||||
if libraries == nil {
|
||||
s.logger.Warn("Libraries is nil in service, converting to empty array")
|
||||
libraries = []VirtualTapeLibrary{}
|
||||
}
|
||||
s.logger.Info("Returning from service", "count", len(libraries), "is_nil", libraries == nil)
|
||||
return libraries, nil
|
||||
}
|
||||
|
||||
// GetLibrary retrieves a library by ID
|
||||
@@ -262,10 +290,12 @@ func (s *Service) GetLibrary(ctx context.Context, id string) (*VirtualTapeLibrar
|
||||
`
|
||||
|
||||
var lib VirtualTapeLibrary
|
||||
var description sql.NullString
|
||||
var createdBy sql.NullString
|
||||
err := s.db.QueryRowContext(ctx, query, id).Scan(
|
||||
&lib.ID, &lib.Name, &lib.Description, &lib.MHVTLibraryID, &lib.BackingStorePath,
|
||||
&lib.ID, &lib.Name, &description, &lib.MHVTLibraryID, &lib.BackingStorePath,
|
||||
&lib.SlotCount, &lib.DriveCount, &lib.IsActive,
|
||||
&lib.CreatedAt, &lib.UpdatedAt, &lib.CreatedBy,
|
||||
&lib.CreatedAt, &lib.UpdatedAt, &createdBy,
|
||||
)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
@@ -274,6 +304,13 @@ func (s *Service) GetLibrary(ctx context.Context, id string) (*VirtualTapeLibrar
|
||||
return nil, fmt.Errorf("failed to get library: %w", err)
|
||||
}
|
||||
|
||||
if description.Valid {
|
||||
lib.Description = description.String
|
||||
}
|
||||
if createdBy.Valid {
|
||||
lib.CreatedBy = createdBy.String
|
||||
}
|
||||
|
||||
return &lib, nil
|
||||
}
|
||||
|
||||
@@ -500,4 +537,3 @@ func (s *Service) DeleteLibrary(ctx context.Context, id string) error {
|
||||
s.logger.Info("Virtual tape library deleted", "id", id, "name", lib.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user