Files
storage-appliance/internal/infra/iscsi/iscsi.go

122 lines
4.4 KiB
Go

package iscsi
import (
"context"
"fmt"
"time"
"github.com/example/storage-appliance/internal/infra/osexec"
)
// Adapter wraps targetcli invocations for LIO (targetcli) management.
type Adapter struct {
Runner osexec.Runner
}
func NewAdapter(runner osexec.Runner) *Adapter { return &Adapter{Runner: runner} }
// CreateTarget creates an IQN target via targetcli
func (a *Adapter) CreateTarget(ctx context.Context, iqn string) error {
// Use a short timeout for cli interactions
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
_, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", "/iscsi", "create", iqn)
if err != nil {
return fmt.Errorf("targetcli create target failed: %v %s", err, stderr)
}
if code != 0 {
return fmt.Errorf("targetcli create returned: %s", stderr)
}
return nil
}
// CreateBackstore creates a block backstore for a zvol device.
func (a *Adapter) CreateBackstore(ctx context.Context, name, devpath string) error {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
// targetcli syntax: /backstores/block create <name> <devpath>
_, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", "/backstores/block", "create", name, devpath)
if err != nil {
return fmt.Errorf("targetcli create backstore failed: %v %s", err, stderr)
}
if code != 0 {
return fmt.Errorf("targetcli backstore returned: %s", stderr)
}
return nil
}
// CreateLUN maps backstore into target's TPG1 LUNs
func (a *Adapter) CreateLUN(ctx context.Context, iqn, backstoreName string, lunID int) error {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
bsPath := fmt.Sprintf("/backstores/block/%s", backstoreName)
tpgPath := fmt.Sprintf("/iscsi/%s/tpg1/luns", iqn)
_, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", tpgPath, "create", bsPath)
if err != nil {
return fmt.Errorf("targetcli create lun failed: %v %s", err, stderr)
}
if code != 0 {
return fmt.Errorf("targetcli create lun returned: %s", stderr)
}
return nil
}
// DeleteLUN unmaps a LUN from a target; if the LUN is mapped fail unless forced.
func (a *Adapter) DeleteLUN(ctx context.Context, iqn string, lunID int) error {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
tpgPath := fmt.Sprintf("/iscsi/%s/tpg1/luns", iqn)
// delete by numeric id
_, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", tpgPath, "delete", fmt.Sprintf("%d", lunID))
if err != nil {
return fmt.Errorf("targetcli delete lun failed: %v %s", err, stderr)
}
if code != 0 {
return fmt.Errorf("targetcli delete lun returned: %s", stderr)
}
return nil
}
func (a *Adapter) AddPortal(ctx context.Context, iqn, address string, port int) error {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
tpgPath := fmt.Sprintf("/iscsi/%s/tpg1/portals", iqn)
addr := fmt.Sprintf("%s:%d", address, port)
_, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", tpgPath, "create", addr)
if err != nil {
return fmt.Errorf("targetcli add portal failed: %v %s", err, stderr)
}
if code != 0 {
return fmt.Errorf("targetcli add portal returned: %s", stderr)
}
return nil
}
func (a *Adapter) AddACL(ctx context.Context, iqn, initiator string) error {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
aclPath := fmt.Sprintf("/iscsi/%s/tpg1/acls", iqn)
_, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", aclPath, "create", initiator)
if err != nil {
return fmt.Errorf("targetcli add acl failed: %v %s", err, stderr)
}
if code != 0 {
return fmt.Errorf("targetcli add acl returned: %s", stderr)
}
return nil
}
// Save writes the configuration to storage (saving targetcli config)
func (a *Adapter) Save(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
_, stderr, code, err := osexec.ExecWithRunner(a.Runner, ctx, "targetcli", "saveconfig")
if err != nil {
return fmt.Errorf("targetcli save failed: %v %s", err, stderr)
}
if code != 0 {
return fmt.Errorf("targetcli save returned: %s", stderr)
}
return nil
}