This commit is contained in:
182
internal/storage/iscsi.go
Normal file
182
internal/storage/iscsi.go
Normal file
@@ -0,0 +1,182 @@
|
||||
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
|
||||
}
|
||||
128
internal/storage/nfs.go
Normal file
128
internal/storage/nfs.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"gitea.avt.data-center.id/othman.suseno/atlas/internal/models"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNFSExportNotFound = errors.New("NFS export not found")
|
||||
ErrNFSExportExists = errors.New("NFS export already exists")
|
||||
)
|
||||
|
||||
// NFSStore manages NFS exports
|
||||
type NFSStore struct {
|
||||
mu sync.RWMutex
|
||||
exports map[string]*models.NFSExport
|
||||
nextID int
|
||||
}
|
||||
|
||||
// NewNFSStore creates a new NFS export store
|
||||
func NewNFSStore() *NFSStore {
|
||||
return &NFSStore{
|
||||
exports: make(map[string]*models.NFSExport),
|
||||
nextID: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// List returns all NFS exports
|
||||
func (s *NFSStore) List() []models.NFSExport {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
exports := make([]models.NFSExport, 0, len(s.exports))
|
||||
for _, export := range s.exports {
|
||||
exports = append(exports, *export)
|
||||
}
|
||||
return exports
|
||||
}
|
||||
|
||||
// Get returns an export by ID
|
||||
func (s *NFSStore) Get(id string) (*models.NFSExport, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
export, ok := s.exports[id]
|
||||
if !ok {
|
||||
return nil, ErrNFSExportNotFound
|
||||
}
|
||||
return export, nil
|
||||
}
|
||||
|
||||
// GetByPath returns an export by path
|
||||
func (s *NFSStore) GetByPath(path string) (*models.NFSExport, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
for _, export := range s.exports {
|
||||
if export.Path == path {
|
||||
return export, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrNFSExportNotFound
|
||||
}
|
||||
|
||||
// Create creates a new NFS export
|
||||
func (s *NFSStore) Create(path, dataset string, clients []string, readOnly, rootSquash bool) (*models.NFSExport, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
// Check if path already exists
|
||||
for _, export := range s.exports {
|
||||
if export.Path == path {
|
||||
return nil, ErrNFSExportExists
|
||||
}
|
||||
}
|
||||
|
||||
id := fmt.Sprintf("nfs-%d", s.nextID)
|
||||
s.nextID++
|
||||
|
||||
export := &models.NFSExport{
|
||||
ID: id,
|
||||
Path: path,
|
||||
Dataset: dataset,
|
||||
Clients: clients,
|
||||
ReadOnly: readOnly,
|
||||
RootSquash: rootSquash,
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
s.exports[id] = export
|
||||
return export, nil
|
||||
}
|
||||
|
||||
// Update updates an existing export
|
||||
func (s *NFSStore) Update(id string, clients []string, readOnly, rootSquash, enabled bool) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
export, ok := s.exports[id]
|
||||
if !ok {
|
||||
return ErrNFSExportNotFound
|
||||
}
|
||||
|
||||
export.ReadOnly = readOnly
|
||||
export.RootSquash = rootSquash
|
||||
export.Enabled = enabled
|
||||
if clients != nil {
|
||||
export.Clients = clients
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete removes an export
|
||||
func (s *NFSStore) Delete(id string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if _, ok := s.exports[id]; !ok {
|
||||
return ErrNFSExportNotFound
|
||||
}
|
||||
|
||||
delete(s.exports, id)
|
||||
return nil
|
||||
}
|
||||
131
internal/storage/smb.go
Normal file
131
internal/storage/smb.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"gitea.avt.data-center.id/othman.suseno/atlas/internal/models"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSMBShareNotFound = errors.New("SMB share not found")
|
||||
ErrSMBShareExists = errors.New("SMB share already exists")
|
||||
)
|
||||
|
||||
// SMBStore manages SMB shares
|
||||
type SMBStore struct {
|
||||
mu sync.RWMutex
|
||||
shares map[string]*models.SMBShare
|
||||
nextID int
|
||||
}
|
||||
|
||||
// NewSMBStore creates a new SMB share store
|
||||
func NewSMBStore() *SMBStore {
|
||||
return &SMBStore{
|
||||
shares: make(map[string]*models.SMBShare),
|
||||
nextID: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// List returns all SMB shares
|
||||
func (s *SMBStore) List() []models.SMBShare {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
shares := make([]models.SMBShare, 0, len(s.shares))
|
||||
for _, share := range s.shares {
|
||||
shares = append(shares, *share)
|
||||
}
|
||||
return shares
|
||||
}
|
||||
|
||||
// Get returns a share by ID
|
||||
func (s *SMBStore) Get(id string) (*models.SMBShare, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
share, ok := s.shares[id]
|
||||
if !ok {
|
||||
return nil, ErrSMBShareNotFound
|
||||
}
|
||||
return share, nil
|
||||
}
|
||||
|
||||
// GetByName returns a share by name
|
||||
func (s *SMBStore) GetByName(name string) (*models.SMBShare, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
for _, share := range s.shares {
|
||||
if share.Name == name {
|
||||
return share, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrSMBShareNotFound
|
||||
}
|
||||
|
||||
// Create creates a new SMB share
|
||||
func (s *SMBStore) Create(name, path, dataset, description string, readOnly, guestOK bool, validUsers []string) (*models.SMBShare, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
// Check if name already exists
|
||||
for _, share := range s.shares {
|
||||
if share.Name == name {
|
||||
return nil, ErrSMBShareExists
|
||||
}
|
||||
}
|
||||
|
||||
id := fmt.Sprintf("smb-%d", s.nextID)
|
||||
s.nextID++
|
||||
|
||||
share := &models.SMBShare{
|
||||
ID: id,
|
||||
Name: name,
|
||||
Path: path,
|
||||
Dataset: dataset,
|
||||
Description: description,
|
||||
ReadOnly: readOnly,
|
||||
GuestOK: guestOK,
|
||||
ValidUsers: validUsers,
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
s.shares[id] = share
|
||||
return share, nil
|
||||
}
|
||||
|
||||
// Update updates an existing share
|
||||
func (s *SMBStore) Update(id, description string, readOnly, guestOK, enabled bool, validUsers []string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
share, ok := s.shares[id]
|
||||
if !ok {
|
||||
return ErrSMBShareNotFound
|
||||
}
|
||||
|
||||
share.Description = description
|
||||
share.ReadOnly = readOnly
|
||||
share.GuestOK = guestOK
|
||||
share.Enabled = enabled
|
||||
if validUsers != nil {
|
||||
share.ValidUsers = validUsers
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete removes a share
|
||||
func (s *SMBStore) Delete(id string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if _, ok := s.shares[id]; !ok {
|
||||
return ErrSMBShareNotFound
|
||||
}
|
||||
|
||||
delete(s.shares, id)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user