Files
BAMS/backend/internal/services/iscsi/service.go

223 lines
5.0 KiB
Go

package iscsi
import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/bams/backend/internal/config"
"github.com/bams/backend/internal/logger"
)
type Target struct {
ID string `json:"id"`
IQN string `json:"iqn"`
Portals []string `json:"portals"`
Initiators []string `json:"initiators"`
LUNs []LUN `json:"luns"`
Status string `json:"status"`
}
type LUN struct {
Number int `json:"number"`
Device string `json:"device"`
Type string `json:"type"` // "disk" or "tape"
}
type Session struct {
TargetIQN string `json:"target_iqn"`
InitiatorIQN string `json:"initiator_iqn"`
IP string `json:"ip"`
State string `json:"state"`
}
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) ListTargets() ([]*Target, error) {
s.logger.Debug("Listing iSCSI targets")
// Read SCST configuration
targets := []*Target{}
// Parse SCST config file
if _, err := os.Stat(s.config.SCSTConfig); err == nil {
data, err := os.ReadFile(s.config.SCSTConfig)
if err == nil {
targets = s.parseSCSTConfig(string(data))
}
}
return targets, nil
}
func (s *Service) GetTarget(id string) (*Target, error) {
targets, err := s.ListTargets()
if err != nil {
return nil, err
}
for _, target := range targets {
if target.ID == id || target.IQN == id {
return target, nil
}
}
return nil, fmt.Errorf("target not found: %s", id)
}
func (s *Service) CreateTarget(iqn string, portals, initiators []string) (*Target, error) {
s.logger.Info("Creating iSCSI target", "iqn", iqn)
target := &Target{
ID: iqn,
IQN: iqn,
Portals: portals,
Initiators: initiators,
LUNs: []LUN{},
Status: "pending",
}
// Write to SCST config
if err := s.writeSCSTConfig(target); err != nil {
return nil, fmt.Errorf("failed to write SCST config: %w", err)
}
return target, nil
}
func (s *Service) UpdateTarget(id string, portals, initiators []string) (*Target, error) {
target, err := s.GetTarget(id)
if err != nil {
return nil, err
}
target.Portals = portals
target.Initiators = initiators
if err := s.writeSCSTConfig(target); err != nil {
return nil, fmt.Errorf("failed to update SCST config: %w", err)
}
return target, nil
}
func (s *Service) DeleteTarget(id string) error {
s.logger.Info("Deleting iSCSI target", "id", id)
// Remove from SCST config
// TODO: Implement actual deletion
return nil
}
func (s *Service) ApplyTarget(id string) error {
s.logger.Info("Applying iSCSI target configuration", "id", id)
// Reload SCST configuration
cmd := exec.Command("scstadmin", "-config", s.config.SCSTConfig)
output, err := cmd.CombinedOutput()
if err != nil {
s.logger.Error("Failed to apply SCST config", "error", string(output))
return fmt.Errorf("failed to apply SCST config: %w", err)
}
return nil
}
func (s *Service) ListSessions() ([]*Session, error) {
s.logger.Debug("Listing iSCSI sessions")
sessions := []*Session{}
// Query SCST for active sessions
cmd := exec.Command("scstadmin", "-list_sessions")
output, err := cmd.CombinedOutput()
if err == nil {
sessions = s.parseSCSTSessions(string(output))
}
return sessions, nil
}
func (s *Service) parseSCSTConfig(data string) []*Target {
targets := []*Target{}
// Simplified parsing - real implementation would parse SCST config format
lines := strings.Split(data, "\n")
currentTarget := &Target{}
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "TARGET") {
if currentTarget.IQN != "" {
targets = append(targets, currentTarget)
}
currentTarget = &Target{
IQN: strings.Fields(line)[1],
ID: strings.Fields(line)[1],
Portals: []string{},
Initiators: []string{},
LUNs: []LUN{},
Status: "active",
}
}
}
if currentTarget.IQN != "" {
targets = append(targets, currentTarget)
}
return targets
}
func (s *Service) parseSCSTSessions(data string) []*Session {
sessions := []*Session{}
// Parse SCST session output
lines := strings.Split(data, "\n")
for _, line := range lines {
if strings.Contains(line, "session") {
// Parse session information
session := &Session{
State: "active",
}
sessions = append(sessions, session)
}
}
return sessions
}
func (s *Service) writeSCSTConfig(target *Target) error {
// Generate SCST configuration
config := fmt.Sprintf(`TARGET %s {
enabled 1
`, target.IQN)
for _, portal := range target.Portals {
config += fmt.Sprintf(" portal %s {\n", portal)
config += " enabled 1\n"
config += " }\n"
}
for _, initiator := range target.Initiators {
config += fmt.Sprintf(" initiator %s {\n", initiator)
config += " enabled 1\n"
config += " }\n"
}
config += "}\n"
// Append to config file (simplified - real implementation would merge properly)
return os.WriteFile(s.config.SCSTConfig, []byte(config), 0644)
}