alpha repo init
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -2046,71 +2047,72 @@ func (a *App) syncSMBSharesFromOS() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// syncISCSITargetsFromOS syncs iSCSI targets from targetcli to the store
|
||||
// syncISCSITargetsFromOS syncs iSCSI targets directly from sysfs (no targetcli needed)
|
||||
func (a *App) syncISCSITargetsFromOS() error {
|
||||
log.Printf("debug: starting syncISCSITargetsFromOS")
|
||||
// Get list of targets from targetcli
|
||||
// Set TARGETCLI_HOME and TARGETCLI_LOCK_DIR to writable directories
|
||||
// Create the directories first if they don't exist
|
||||
os.MkdirAll("/tmp/.targetcli", 0755)
|
||||
os.MkdirAll("/tmp/targetcli-run", 0755)
|
||||
// Service runs as root, no need for sudo
|
||||
cmd := exec.Command("sh", "-c", "TARGETCLI_HOME=/tmp/.targetcli TARGETCLI_LOCK_DIR=/tmp/targetcli-run targetcli /iscsi ls")
|
||||
output, err := cmd.CombinedOutput()
|
||||
log.Printf("debug: starting syncISCSITargetsFromOS - reading from sysfs")
|
||||
|
||||
// Read iSCSI targets directly from /sys/kernel/config/target/iscsi/
|
||||
// This avoids targetcli lock file issues
|
||||
iscsiPath := "/sys/kernel/config/target/iscsi"
|
||||
|
||||
entries, err := os.ReadDir(iscsiPath)
|
||||
if err != nil {
|
||||
// Log the error but don't fail - targetcli might not be configured
|
||||
log.Printf("warning: failed to list iSCSI targets from targetcli: %v (output: %s)", err, string(output))
|
||||
log.Printf("warning: failed to read iSCSI config directory: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("debug: targetcli output: %s", string(output))
|
||||
lines := strings.Split(string(output), "\n")
|
||||
var currentIQN string
|
||||
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if this is a target line (starts with "o- iqn.")
|
||||
if strings.HasPrefix(line, "o- iqn.") {
|
||||
log.Printf("debug: found target line: %s", line)
|
||||
// Extract IQN from line like "o- iqn.2025-12.com.atlas:target-1"
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 2 {
|
||||
currentIQN = parts[1]
|
||||
// Check if this is an IQN (starts with "iqn.")
|
||||
iqn := entry.Name()
|
||||
if !strings.HasPrefix(iqn, "iqn.") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if target already exists in store
|
||||
existingTargets := a.iscsiStore.List()
|
||||
exists := false
|
||||
for _, t := range existingTargets {
|
||||
if t.IQN == currentIQN {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
log.Printf("debug: found iSCSI target: %s", iqn)
|
||||
|
||||
// Check if target already exists in store
|
||||
existingTargets := a.iscsiStore.List()
|
||||
exists := false
|
||||
for _, t := range existingTargets {
|
||||
if t.IQN == iqn {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if exists {
|
||||
log.Printf("debug: target %s already in store, skipping", iqn)
|
||||
// Still sync LUNs in case they changed
|
||||
target, err := a.iscsiStore.GetByIQN(iqn)
|
||||
if err == nil {
|
||||
if err := a.syncLUNsFromOS(iqn, target.ID, target.Type); err != nil {
|
||||
log.Printf("warning: failed to sync LUNs for target %s: %v", iqn, err)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !exists {
|
||||
// Try to determine target type from IQN
|
||||
targetType := models.ISCSITargetTypeDisk // Default to disk mode
|
||||
if strings.Contains(strings.ToLower(currentIQN), "tape") {
|
||||
targetType = models.ISCSITargetTypeTape
|
||||
}
|
||||
// Try to determine target type from IQN
|
||||
targetType := models.ISCSITargetTypeDisk // Default to disk mode
|
||||
if strings.Contains(strings.ToLower(iqn), "tape") {
|
||||
targetType = models.ISCSITargetTypeTape
|
||||
}
|
||||
|
||||
// Create target in store
|
||||
target, err := a.iscsiStore.CreateWithType(currentIQN, targetType, []string{})
|
||||
if err != nil && err != storage.ErrISCSITargetExists {
|
||||
log.Printf("warning: failed to sync iSCSI target %s: %v", currentIQN, err)
|
||||
} else if err == nil {
|
||||
log.Printf("synced iSCSI target from OS: %s (type: %s)", currentIQN, targetType)
|
||||
// Create target in store
|
||||
target, err := a.iscsiStore.CreateWithType(iqn, targetType, []string{})
|
||||
if err != nil && err != storage.ErrISCSITargetExists {
|
||||
log.Printf("warning: failed to sync iSCSI target %s: %v", iqn, err)
|
||||
continue
|
||||
} else if err == nil {
|
||||
log.Printf("synced iSCSI target from OS: %s (type: %s)", iqn, targetType)
|
||||
|
||||
// Now try to sync LUNs for this target
|
||||
if err := a.syncLUNsFromOS(currentIQN, target.ID, targetType); err != nil {
|
||||
log.Printf("warning: failed to sync LUNs for target %s: %v", currentIQN, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now try to sync LUNs for this target
|
||||
if err := a.syncLUNsFromOS(iqn, target.ID, targetType); err != nil {
|
||||
log.Printf("warning: failed to sync LUNs for target %s: %v", iqn, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2118,119 +2120,163 @@ func (a *App) syncISCSITargetsFromOS() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// syncLUNsFromOS syncs LUNs for a specific target from targetcli
|
||||
// syncLUNsFromOS syncs LUNs for a specific target directly from sysfs (no targetcli needed)
|
||||
func (a *App) syncLUNsFromOS(iqn, targetID string, targetType models.ISCSITargetType) error {
|
||||
// Get LUNs for this target
|
||||
// Service runs as root, no need for sudo
|
||||
cmd := exec.Command("sh", "-c", "TARGETCLI_HOME=/tmp/.targetcli TARGETCLI_LOCK_DIR=/tmp/targetcli-run targetcli /iscsi/"+iqn+"/tpg1/luns ls")
|
||||
output, err := cmd.CombinedOutput()
|
||||
log.Printf("debug: syncing LUNs for target %s from sysfs", iqn)
|
||||
|
||||
// Read LUNs directly from /sys/kernel/config/target/iscsi/{iqn}/tpgt_1/lun/
|
||||
tpgtPath := fmt.Sprintf("/sys/kernel/config/target/iscsi/%s/tpgt_1/lun", iqn)
|
||||
|
||||
entries, err := os.ReadDir(tpgtPath)
|
||||
if err != nil {
|
||||
// No LUNs or can't read - that's okay, log for debugging
|
||||
log.Printf("debug: failed to list LUNs for target %s: %v (output: %s)", iqn, err, string(output))
|
||||
log.Printf("debug: no LUNs directory found for target %s: %v", iqn, err)
|
||||
return nil // No LUNs is okay
|
||||
}
|
||||
|
||||
// Get target to check existing LUNs
|
||||
target, err := a.iscsiStore.Get(targetID)
|
||||
if err != nil {
|
||||
log.Printf("warning: target %s not found in store", targetID)
|
||||
return nil
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, "o- lun") {
|
||||
// Parse LUN line like "o- lun0 ....................................... [block/pool-test-02-vol01 (/dev/zvol/pool-test-02/vol01) (default_tg_pt_gp)]"
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 2 {
|
||||
// Extract LUN ID from "lun0"
|
||||
lunIDStr := strings.TrimPrefix(parts[1], "lun")
|
||||
lunID, err := strconv.Atoi(lunIDStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Extract backstore path and device from the line
|
||||
var backstorePath string
|
||||
var devicePath string
|
||||
var zvolName string
|
||||
// Check if this is a LUN directory (starts with "lun_")
|
||||
lunDirName := entry.Name()
|
||||
if !strings.HasPrefix(lunDirName, "lun_") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Find the part with brackets - might span multiple parts
|
||||
fullLine := strings.Join(parts, " ")
|
||||
start := strings.Index(fullLine, "[")
|
||||
end := strings.LastIndex(fullLine, "]")
|
||||
if start >= 0 && end > start {
|
||||
content := fullLine[start+1 : end]
|
||||
// Parse content like "block/pool-test-02-vol01 (/dev/zvol/pool-test-02/vol01)"
|
||||
if strings.Contains(content, "(") {
|
||||
// Has device path
|
||||
parts2 := strings.Split(content, "(")
|
||||
if len(parts2) >= 2 {
|
||||
backstorePath = strings.TrimSpace(parts2[0])
|
||||
devicePath = strings.Trim(strings.TrimSpace(parts2[1]), "()")
|
||||
// Extract LUN ID from "lun_0", "lun_1", etc.
|
||||
lunIDStr := strings.TrimPrefix(lunDirName, "lun_")
|
||||
lunID, err := strconv.Atoi(lunIDStr)
|
||||
if err != nil {
|
||||
log.Printf("debug: invalid LUN directory name: %s", lunDirName)
|
||||
continue
|
||||
}
|
||||
|
||||
// If device is a zvol, extract ZVOL name
|
||||
if strings.HasPrefix(devicePath, "/dev/zvol/") {
|
||||
zvolName = strings.TrimPrefix(devicePath, "/dev/zvol/")
|
||||
}
|
||||
}
|
||||
// Check if LUN already exists
|
||||
lunExists := false
|
||||
for _, lun := range target.LUNs {
|
||||
if lun.ID == lunID {
|
||||
lunExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if lunExists {
|
||||
log.Printf("debug: LUN %d already exists for target %s", lunID, iqn)
|
||||
continue
|
||||
}
|
||||
|
||||
// Find storage_object symlink in LUN directory
|
||||
// Structure: /sys/kernel/config/target/iscsi/{iqn}/tpgt_1/lun/lun_0/{hash} -> ../../../../../../target/core/iblock_0/{name}
|
||||
lunPath := fmt.Sprintf("%s/%s", tpgtPath, lunDirName)
|
||||
storageObjectPath := ""
|
||||
|
||||
// Look for symlink in subdirectories (the hash directory that links to backstore)
|
||||
subEntries, err := os.ReadDir(lunPath)
|
||||
if err != nil {
|
||||
log.Printf("debug: failed to read LUN directory %s: %v", lunPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, subEntry := range subEntries {
|
||||
// Check if this is a symlink (the hash directory that links to backstore)
|
||||
subEntryPath := fmt.Sprintf("%s/%s", lunPath, subEntry.Name())
|
||||
fileInfo, err := os.Lstat(subEntryPath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if it's a symlink
|
||||
if fileInfo.Mode()&os.ModeSymlink != 0 {
|
||||
if linkTarget, err := os.Readlink(subEntryPath); err == nil {
|
||||
// Resolve to absolute path
|
||||
if strings.HasPrefix(linkTarget, "/") {
|
||||
storageObjectPath = linkTarget
|
||||
} else {
|
||||
backstorePath = content
|
||||
// Relative path, resolve it
|
||||
absPath, err := filepath.Abs(fmt.Sprintf("%s/%s", lunPath, linkTarget))
|
||||
if err == nil {
|
||||
storageObjectPath = absPath
|
||||
} else {
|
||||
// Try resolving relative to parent directory
|
||||
storageObjectPath = filepath.Clean(fmt.Sprintf("%s/%s", filepath.Dir(lunPath), linkTarget))
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if LUN already exists
|
||||
target, err := a.iscsiStore.Get(targetID)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if storageObjectPath == "" {
|
||||
log.Printf("debug: no storage_object symlink found for LUN %d in target %s", lunID, iqn)
|
||||
continue
|
||||
}
|
||||
|
||||
lunExists := false
|
||||
for _, lun := range target.LUNs {
|
||||
if lun.ID == lunID {
|
||||
lunExists = true
|
||||
// Read device path from storage_object/udev_path
|
||||
udevPathFile := fmt.Sprintf("%s/udev_path", storageObjectPath)
|
||||
devicePath := ""
|
||||
if udevPathBytes, err := os.ReadFile(udevPathFile); err == nil {
|
||||
devicePath = strings.TrimSpace(string(udevPathBytes))
|
||||
} else {
|
||||
log.Printf("debug: failed to read udev_path from %s: %v", udevPathFile, err)
|
||||
}
|
||||
|
||||
// Determine backstore type from path
|
||||
backstoreType := "block"
|
||||
if strings.Contains(storageObjectPath, "/pscsi/") {
|
||||
backstoreType = "pscsi"
|
||||
} else if strings.Contains(storageObjectPath, "/fileio/") {
|
||||
backstoreType = "fileio"
|
||||
}
|
||||
|
||||
// Extract ZVOL name if device is a zvol
|
||||
var zvolName string
|
||||
var size uint64
|
||||
if strings.HasPrefix(devicePath, "/dev/zvol/") {
|
||||
zvolName = strings.TrimPrefix(devicePath, "/dev/zvol/")
|
||||
// Get size from ZFS
|
||||
zvols, err := a.zfs.ListZVOLs("")
|
||||
if err == nil {
|
||||
for _, zvol := range zvols {
|
||||
if zvol.Name == zvolName {
|
||||
size = zvol.Size
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !lunExists {
|
||||
// Determine backstore type
|
||||
backstoreType := "block"
|
||||
if strings.HasPrefix(backstorePath, "pscsi/") {
|
||||
backstoreType = "pscsi"
|
||||
} else if strings.HasPrefix(backstorePath, "fileio/") {
|
||||
backstoreType = "fileio"
|
||||
}
|
||||
|
||||
// Get size if it's a ZVOL
|
||||
var size uint64
|
||||
if zvolName != "" {
|
||||
zvols, err := a.zfs.ListZVOLs("")
|
||||
if err == nil {
|
||||
for _, zvol := range zvols {
|
||||
if zvol.Name == zvolName {
|
||||
size = zvol.Size
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add LUN to store
|
||||
if targetType == models.ISCSITargetTypeTape && devicePath != "" {
|
||||
// Tape mode: use device
|
||||
_, err := a.iscsiStore.AddLUNWithDevice(targetID, "", devicePath, size, backstoreType, "")
|
||||
if err != nil && err != storage.ErrLUNExists {
|
||||
log.Printf("warning: failed to sync LUN %d for target %s: %v", lunID, iqn, err)
|
||||
}
|
||||
} else if zvolName != "" {
|
||||
// Disk mode: use ZVOL
|
||||
_, err := a.iscsiStore.AddLUNWithDevice(targetID, zvolName, "", size, backstoreType, "")
|
||||
if err != nil && err != storage.ErrLUNExists {
|
||||
log.Printf("warning: failed to sync LUN %d for target %s: %v", lunID, iqn, err)
|
||||
}
|
||||
} else if devicePath != "" {
|
||||
// Generic device
|
||||
_, err := a.iscsiStore.AddLUNWithDevice(targetID, "", devicePath, size, backstoreType, "")
|
||||
if err != nil && err != storage.ErrLUNExists {
|
||||
log.Printf("warning: failed to sync LUN %d for target %s: %v", lunID, iqn, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add LUN to store
|
||||
if targetType == models.ISCSITargetTypeTape && devicePath != "" {
|
||||
// Tape mode: use device
|
||||
_, err := a.iscsiStore.AddLUNWithDevice(targetID, "", devicePath, size, backstoreType, "")
|
||||
if err != nil && err != storage.ErrLUNExists {
|
||||
log.Printf("warning: failed to sync LUN %d for target %s: %v", lunID, iqn, err)
|
||||
} else if err == nil {
|
||||
log.Printf("synced LUN %d from OS for target %s (device: %s)", lunID, iqn, devicePath)
|
||||
}
|
||||
} else if zvolName != "" {
|
||||
// Disk mode: use ZVOL
|
||||
_, err := a.iscsiStore.AddLUNWithDevice(targetID, zvolName, "", size, backstoreType, "")
|
||||
if err != nil && err != storage.ErrLUNExists {
|
||||
log.Printf("warning: failed to sync LUN %d for target %s: %v", lunID, iqn, err)
|
||||
} else if err == nil {
|
||||
log.Printf("synced LUN %d from OS for target %s (zvol: %s)", lunID, iqn, zvolName)
|
||||
}
|
||||
} else if devicePath != "" {
|
||||
// Generic device
|
||||
_, err := a.iscsiStore.AddLUNWithDevice(targetID, "", devicePath, size, backstoreType, "")
|
||||
if err != nil && err != storage.ErrLUNExists {
|
||||
log.Printf("warning: failed to sync LUN %d for target %s: %v", lunID, iqn, err)
|
||||
} else if err == nil {
|
||||
log.Printf("synced LUN %d from OS for target %s (device: %s)", lunID, iqn, devicePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user