122 lines
4.4 KiB
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
|
|
}
|