245 lines
5.3 KiB
Go
245 lines
5.3 KiB
Go
package storage
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
|
|
"gitea.avt.data-center.id/othman.suseno/atlas/internal/models"
|
|
)
|
|
|
|
var (
|
|
ErrISCSITargetNotFound = errors.New("iSCSI target not found")
|
|
ErrISCSITargetExists = errors.New("iSCSI target already exists")
|
|
ErrLUNNotFound = errors.New("LUN not found")
|
|
ErrLUNExists = errors.New("LUN already exists")
|
|
)
|
|
|
|
// ISCSIStore manages iSCSI targets and LUNs
|
|
type ISCSIStore struct {
|
|
mu sync.RWMutex
|
|
targets map[string]*models.ISCSITarget
|
|
nextID int
|
|
}
|
|
|
|
// NewISCSIStore creates a new iSCSI store
|
|
func NewISCSIStore() *ISCSIStore {
|
|
return &ISCSIStore{
|
|
targets: make(map[string]*models.ISCSITarget),
|
|
nextID: 1,
|
|
}
|
|
}
|
|
|
|
// List returns all iSCSI targets
|
|
func (s *ISCSIStore) List() []models.ISCSITarget {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
|
|
targets := make([]models.ISCSITarget, 0, len(s.targets))
|
|
for _, target := range s.targets {
|
|
targets = append(targets, *target)
|
|
}
|
|
return targets
|
|
}
|
|
|
|
// Get returns a target by ID
|
|
func (s *ISCSIStore) Get(id string) (*models.ISCSITarget, error) {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
|
|
target, ok := s.targets[id]
|
|
if !ok {
|
|
return nil, ErrISCSITargetNotFound
|
|
}
|
|
return target, nil
|
|
}
|
|
|
|
// GetByIQN returns a target by IQN
|
|
func (s *ISCSIStore) GetByIQN(iqn string) (*models.ISCSITarget, error) {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
|
|
for _, target := range s.targets {
|
|
if target.IQN == iqn {
|
|
return target, nil
|
|
}
|
|
}
|
|
return nil, ErrISCSITargetNotFound
|
|
}
|
|
|
|
// Create creates a new iSCSI target
|
|
func (s *ISCSIStore) Create(iqn string, initiators []string) (*models.ISCSITarget, error) {
|
|
// Default to disk mode for backward compatibility
|
|
return s.CreateWithType(iqn, models.ISCSITargetTypeDisk, initiators)
|
|
}
|
|
|
|
// CreateWithType creates a new iSCSI target with specified type
|
|
func (s *ISCSIStore) CreateWithType(iqn string, targetType models.ISCSITargetType, initiators []string) (*models.ISCSITarget, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
// Check if IQN already exists
|
|
for _, target := range s.targets {
|
|
if target.IQN == iqn {
|
|
return nil, ErrISCSITargetExists
|
|
}
|
|
}
|
|
|
|
id := fmt.Sprintf("iscsi-%d", s.nextID)
|
|
s.nextID++
|
|
|
|
target := &models.ISCSITarget{
|
|
ID: id,
|
|
IQN: iqn,
|
|
Type: targetType,
|
|
LUNs: []models.LUN{},
|
|
Initiators: initiators,
|
|
Enabled: true,
|
|
}
|
|
|
|
s.targets[id] = target
|
|
return target, nil
|
|
}
|
|
|
|
// Update updates an existing target
|
|
func (s *ISCSIStore) Update(id string, initiators []string, enabled bool) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
target, ok := s.targets[id]
|
|
if !ok {
|
|
return ErrISCSITargetNotFound
|
|
}
|
|
|
|
target.Enabled = enabled
|
|
if initiators != nil {
|
|
target.Initiators = initiators
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Delete removes a target
|
|
func (s *ISCSIStore) Delete(id string) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if _, ok := s.targets[id]; !ok {
|
|
return ErrISCSITargetNotFound
|
|
}
|
|
|
|
delete(s.targets, id)
|
|
return nil
|
|
}
|
|
|
|
// AddLUN adds a LUN to a target
|
|
func (s *ISCSIStore) AddLUN(targetID string, zvol string, size uint64) (*models.LUN, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
target, ok := s.targets[targetID]
|
|
if !ok {
|
|
return nil, ErrISCSITargetNotFound
|
|
}
|
|
|
|
// Check if ZVOL already mapped
|
|
for _, lun := range target.LUNs {
|
|
if lun.ZVOL == zvol {
|
|
return nil, ErrLUNExists
|
|
}
|
|
}
|
|
|
|
// Find next available LUN ID
|
|
lunID := 0
|
|
for _, lun := range target.LUNs {
|
|
if lun.ID >= lunID {
|
|
lunID = lun.ID + 1
|
|
}
|
|
}
|
|
|
|
lun := models.LUN{
|
|
ID: lunID,
|
|
ZVOL: zvol,
|
|
Size: size,
|
|
Backend: "zvol",
|
|
Backstore: "block", // Default to block for ZVOL
|
|
}
|
|
|
|
target.LUNs = append(target.LUNs, lun)
|
|
return &lun, nil
|
|
}
|
|
|
|
// AddLUNWithDevice adds a LUN to a target with support for device and backstore type
|
|
func (s *ISCSIStore) AddLUNWithDevice(targetID string, zvol string, device string, size uint64, backstore string, backstoreName string) (*models.LUN, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
target, ok := s.targets[targetID]
|
|
if !ok {
|
|
return nil, ErrISCSITargetNotFound
|
|
}
|
|
|
|
// Check if device/ZVOL already mapped
|
|
for _, lun := range target.LUNs {
|
|
if (zvol != "" && lun.ZVOL == zvol) || (device != "" && lun.Device == device) {
|
|
return nil, ErrLUNExists
|
|
}
|
|
}
|
|
|
|
// Find next available LUN ID
|
|
lunID := 0
|
|
for _, lun := range target.LUNs {
|
|
if lun.ID >= lunID {
|
|
lunID = lun.ID + 1
|
|
}
|
|
}
|
|
|
|
// Auto-detect backstore type if not specified
|
|
if backstore == "" {
|
|
if device != "" {
|
|
// If device is specified and looks like tape device, use pscsi
|
|
if strings.HasPrefix(device, "/dev/st") || strings.HasPrefix(device, "/dev/nst") {
|
|
backstore = "pscsi"
|
|
} else {
|
|
backstore = "block"
|
|
}
|
|
} else if zvol != "" {
|
|
backstore = "block"
|
|
}
|
|
}
|
|
|
|
lun := models.LUN{
|
|
ID: lunID,
|
|
ZVOL: zvol,
|
|
Device: device,
|
|
Size: size,
|
|
Backend: "zvol", // Keep for backward compatibility
|
|
Backstore: backstore,
|
|
BackstoreName: backstoreName,
|
|
}
|
|
|
|
target.LUNs = append(target.LUNs, lun)
|
|
return &lun, nil
|
|
}
|
|
|
|
// RemoveLUN removes a LUN from a target
|
|
func (s *ISCSIStore) RemoveLUN(targetID string, lunID int) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
target, ok := s.targets[targetID]
|
|
if !ok {
|
|
return ErrISCSITargetNotFound
|
|
}
|
|
|
|
for i, lun := range target.LUNs {
|
|
if lun.ID == lunID {
|
|
target.LUNs = append(target.LUNs[:i], target.LUNs[i+1:]...)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return ErrLUNNotFound
|
|
}
|