223 lines
5.0 KiB
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)
|
|
}
|