package storage import ( "errors" "fmt" "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) { 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, 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", } 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 }