BAMS initial project structure
This commit is contained in:
183
backend/internal/services/tape/service.go
Normal file
183
backend/internal/services/tape/service.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package tape
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/bams/backend/internal/config"
|
||||
"github.com/bams/backend/internal/logger"
|
||||
)
|
||||
|
||||
type Library struct {
|
||||
Status string `json:"status"`
|
||||
TotalSlots int `json:"total_slots"`
|
||||
ActiveDrives int `json:"active_drives"`
|
||||
Model string `json:"model"`
|
||||
Serial string `json:"serial"`
|
||||
}
|
||||
|
||||
type Drive struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
LoadedTape string `json:"loaded_tape,omitempty"`
|
||||
Barcode string `json:"barcode,omitempty"`
|
||||
Position int `json:"position"`
|
||||
}
|
||||
|
||||
type Slot struct {
|
||||
Number int `json:"number"`
|
||||
Barcode string `json:"barcode,omitempty"`
|
||||
Status string `json:"status"` // "empty", "loaded", "import"
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
config *config.Config
|
||||
logger *logger.Logger
|
||||
}
|
||||
|
||||
func NewService(cfg *config.Config, log *logger.Logger) *Service {
|
||||
return &Service{
|
||||
config: cfg,
|
||||
logger: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) GetLibrary() (*Library, error) {
|
||||
s.logger.Debug("Getting tape library info")
|
||||
|
||||
// Try to detect library using mtx or sg_lib
|
||||
library := &Library{
|
||||
Status: "unknown",
|
||||
TotalSlots: 0,
|
||||
ActiveDrives: 0,
|
||||
}
|
||||
|
||||
// Check for mtx (media changer)
|
||||
cmd := exec.Command("mtx", "-f", "/dev/sg0", "status")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
// Parse mtx output
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "Storage Element") {
|
||||
library.TotalSlots++
|
||||
}
|
||||
if strings.Contains(line, "Data Transfer Element") {
|
||||
library.ActiveDrives++
|
||||
}
|
||||
}
|
||||
library.Status = "online"
|
||||
}
|
||||
|
||||
return library, nil
|
||||
}
|
||||
|
||||
func (s *Service) RunInventory() error {
|
||||
s.logger.Info("Running tape library inventory")
|
||||
|
||||
// Use mtx to inventory
|
||||
cmd := exec.Command("mtx", "-f", "/dev/sg0", "inventory")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to run inventory", "error", string(output))
|
||||
return fmt.Errorf("inventory failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) ListDrives() ([]*Drive, error) {
|
||||
s.logger.Debug("Listing tape drives")
|
||||
|
||||
drives := []*Drive{}
|
||||
|
||||
// Detect drives (up to 8)
|
||||
for i := 0; i < 8; i++ {
|
||||
device := fmt.Sprintf("/dev/nst%d", i)
|
||||
cmd := exec.Command("mt", "-f", device, "status")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
drive := &Drive{
|
||||
ID: fmt.Sprintf("drive-%d", i),
|
||||
Status: "online",
|
||||
Position: i,
|
||||
}
|
||||
|
||||
// Check if tape is loaded
|
||||
if strings.Contains(string(output), "ONLINE") {
|
||||
drive.Status = "loaded"
|
||||
}
|
||||
|
||||
drives = append(drives, drive)
|
||||
}
|
||||
}
|
||||
|
||||
return drives, nil
|
||||
}
|
||||
|
||||
func (s *Service) LoadTape(driveID string, slot int) error {
|
||||
s.logger.Info("Loading tape", "drive", driveID, "slot", slot)
|
||||
|
||||
// Extract drive number from ID
|
||||
driveNum := 0
|
||||
fmt.Sscanf(driveID, "drive-%d", &driveNum)
|
||||
|
||||
// Use mtx to load
|
||||
cmd := exec.Command("mtx", "-f", "/dev/sg0", "load", strconv.Itoa(slot), strconv.Itoa(driveNum))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to load tape", "error", string(output))
|
||||
return fmt.Errorf("load failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) UnloadTape(driveID string, slot int) error {
|
||||
s.logger.Info("Unloading tape", "drive", driveID, "slot", slot)
|
||||
|
||||
// Extract drive number from ID
|
||||
driveNum := 0
|
||||
fmt.Sscanf(driveID, "drive-%d", &driveNum)
|
||||
|
||||
// Use mtx to unload
|
||||
cmd := exec.Command("mtx", "-f", "/dev/sg0", "unload", strconv.Itoa(slot), strconv.Itoa(driveNum))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to unload tape", "error", string(output))
|
||||
return fmt.Errorf("unload failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) ListSlots() ([]*Slot, error) {
|
||||
s.logger.Debug("Listing tape slots")
|
||||
|
||||
slots := []*Slot{}
|
||||
|
||||
// Use mtx to get slot status
|
||||
cmd := exec.Command("mtx", "-f", "/dev/sg0", "status")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return slots, fmt.Errorf("failed to get slot status: %w", err)
|
||||
}
|
||||
|
||||
// Parse mtx output
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "Storage Element") {
|
||||
// Parse slot information
|
||||
slot := &Slot{
|
||||
Status: "empty",
|
||||
}
|
||||
// Extract slot number and barcode from line
|
||||
// This is simplified - real parsing would be more complex
|
||||
slots = append(slots, slot)
|
||||
}
|
||||
}
|
||||
|
||||
return slots, nil
|
||||
}
|
||||
Reference in New Issue
Block a user