184 lines
4.3 KiB
Go
184 lines
4.3 KiB
Go
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
|
|
}
|