diff --git a/dist/adastra-vtl-installer-1.0.0.tar.gz b/dist/adastra-vtl-installer-1.0.0.tar.gz index ed3d55e..049f1d2 100644 Binary files a/dist/adastra-vtl-installer-1.0.0.tar.gz and b/dist/adastra-vtl-installer-1.0.0.tar.gz differ diff --git a/dist/adastra-vtl-installer/VERSION b/dist/adastra-vtl-installer/VERSION index f5d15b9..191c049 100644 --- a/dist/adastra-vtl-installer/VERSION +++ b/dist/adastra-vtl-installer/VERSION @@ -1,4 +1,4 @@ Adastra VTL Installer Version: 1.0.0 -Build Date: 2025-12-10 14:40:16 +Build Date: 2025-12-10 14:48:16 Build Host: vtl-dev diff --git a/dist/adastra-vtl-installer/web-ui/api.php b/dist/adastra-vtl-installer/web-ui/api.php index 5a6a2cb..48038c5 100644 --- a/dist/adastra-vtl-installer/web-ui/api.php +++ b/dist/adastra-vtl-installer/web-ui/api.php @@ -177,7 +177,8 @@ switch ($action) { function listTargets() { $output = []; $returnCode = 0; - exec('sudo tgtadm --lld iscsi --mode target --op show 2>&1', $output, $returnCode); + // Use absolute path + exec('sudo /usr/sbin/tgtadm --lld iscsi --mode target --op show 2>&1', $output, $returnCode); if ($returnCode !== 0) { echo json_encode([ @@ -189,34 +190,86 @@ function listTargets() { $targets = []; $currentTarget = null; - $inACLSection = false; + $currentSession = null; + $section = ''; // 'acl', 'nexus', 'account', etc. foreach ($output as $line) { + // Check for new Target start if (preg_match('/^Target (\d+): (.+)$/', $line, $matches)) { + // Save previous target and session if ($currentTarget) { + if ($currentSession) { + $currentTarget['sessions'][] = $currentSession; + $currentSession = null; + } $targets[] = $currentTarget; } $currentTarget = [ 'tid' => intval($matches[1]), 'name' => trim($matches[2]), 'luns' => 0, - 'acls' => 0 + 'acls' => 0, + 'sessions' => [] ]; - $inACLSection = false; - } elseif ($currentTarget && preg_match('/^\s+LUN: (\d+)/', $line)) { - $currentTarget['luns']++; - $inACLSection = false; - } elseif ($currentTarget && preg_match('/^\s+ACL information:/', $line)) { - $inACLSection = true; - } elseif ($currentTarget && $inACLSection && preg_match('/^\s+(.+)$/', $line, $matches)) { - $acl = trim($matches[1]); - if (!empty($acl) && !preg_match('/^(Account|I_T nexus|LUN|System)/', $acl)) { - $currentTarget['acls']++; + $section = ''; + } elseif ($currentTarget) { + // Check for LUNs (also simple check for count) + if (preg_match('/^\s+LUN: (\d+)/', $line)) { + $currentTarget['luns']++; + // If we were parsing a session, save it as LUN info usually means we left nexus section or are in LUN section + if ($currentSession) { + $currentTarget['sessions'][] = $currentSession; + $currentSession = null; + } + } + // Section Headers + elseif (preg_match('/^\s+ACL information:/', $line)) { + $section = 'acl'; + if ($currentSession) { $currentTarget['sessions'][] = $currentSession; $currentSession = null; } + } + elseif (preg_match('/^\s+I_T nexus information:/', $line)) { + $section = 'nexus'; + } + elseif (preg_match('/^\s+Account information:/', $line)) { + $section = 'account'; // Ignore or handle if needed + if ($currentSession) { $currentTarget['sessions'][] = $currentSession; $currentSession = null; } + } + + // ACL Section Content + elseif ($section === 'acl' && preg_match('/^\s+(.+)$/', $line, $matches)) { + $content = trim($matches[1]); + // Filter out headers if regex matches them by accident (though section logic prevents this usually) + if (!empty($content) && !preg_match('/^(Account|I_T nexus|LUN|System)/', $content)) { + $currentTarget['acls']++; + } + } + + // I_T Nexus Section Content (Sessions) + elseif ($section === 'nexus') { + if (preg_match('/^\s+I_T nexus: (\d+)/', $line, $matches)) { + // New session found + if ($currentSession) { + $currentTarget['sessions'][] = $currentSession; + } + $currentSession = [ + 'nexus_id' => $matches[1], + 'initiator' => 'Unknown', + 'ip' => 'Unknown' + ]; + } elseif ($currentSession && preg_match('/^\s+Initiator: (.+)$/', $line, $matches)) { + $currentSession['initiator'] = trim($matches[1]); + } elseif ($currentSession && preg_match('/^\s+IP Address: (.+)$/', $line, $matches)) { + $currentSession['ip'] = trim($matches[1]); + } } } } + // Save last items if ($currentTarget) { + if ($currentSession) { + $currentTarget['sessions'][] = $currentSession; + } $targets[] = $currentTarget; } diff --git a/dist/adastra-vtl-installer/web-ui/index.html b/dist/adastra-vtl-installer/web-ui/index.html index 81ea183..c28eb57 100644 --- a/dist/adastra-vtl-installer/web-ui/index.html +++ b/dist/adastra-vtl-installer/web-ui/index.html @@ -404,6 +404,32 @@ +
+
+

👥 Active Client Sessions

+ +
+
+ + + + + + + + + + + +
TargetInitiator Name (Client)IP Address
+ +
+
+

âž• Create New Target

diff --git a/dist/adastra-vtl-installer/web-ui/script.js b/dist/adastra-vtl-installer/web-ui/script.js index ca4832d..0c95e4e 100644 --- a/dist/adastra-vtl-installer/web-ui/script.js +++ b/dist/adastra-vtl-installer/web-ui/script.js @@ -1029,6 +1029,38 @@ function loadTargets() { `).join(''); + + // Update Active Sessions Table + const sessionTbody = document.getElementById('iscsi-sessions-body'); + const noSessionsMsg = document.getElementById('no-sessions-msg'); + let hasSessions = false; + + if (sessionTbody) { + let sessionsHtml = ''; + data.targets.forEach(target => { + if (target.sessions && target.sessions.length > 0) { + target.sessions.forEach(session => { + hasSessions = true; + sessionsHtml += ` + + + TID ${target.tid}
+ ${target.name} + + ${session.initiator} + ${session.ip} + + `; + }); + } + }); + + sessionTbody.innerHTML = sessionsHtml; + + if (noSessionsMsg) { + noSessionsMsg.style.display = hasSessions ? 'none' : 'block'; + } + } } } else { showNotification(data.error, 'error'); diff --git a/web-ui/api.php b/web-ui/api.php index 5a6a2cb..48038c5 100644 --- a/web-ui/api.php +++ b/web-ui/api.php @@ -177,7 +177,8 @@ switch ($action) { function listTargets() { $output = []; $returnCode = 0; - exec('sudo tgtadm --lld iscsi --mode target --op show 2>&1', $output, $returnCode); + // Use absolute path + exec('sudo /usr/sbin/tgtadm --lld iscsi --mode target --op show 2>&1', $output, $returnCode); if ($returnCode !== 0) { echo json_encode([ @@ -189,34 +190,86 @@ function listTargets() { $targets = []; $currentTarget = null; - $inACLSection = false; + $currentSession = null; + $section = ''; // 'acl', 'nexus', 'account', etc. foreach ($output as $line) { + // Check for new Target start if (preg_match('/^Target (\d+): (.+)$/', $line, $matches)) { + // Save previous target and session if ($currentTarget) { + if ($currentSession) { + $currentTarget['sessions'][] = $currentSession; + $currentSession = null; + } $targets[] = $currentTarget; } $currentTarget = [ 'tid' => intval($matches[1]), 'name' => trim($matches[2]), 'luns' => 0, - 'acls' => 0 + 'acls' => 0, + 'sessions' => [] ]; - $inACLSection = false; - } elseif ($currentTarget && preg_match('/^\s+LUN: (\d+)/', $line)) { - $currentTarget['luns']++; - $inACLSection = false; - } elseif ($currentTarget && preg_match('/^\s+ACL information:/', $line)) { - $inACLSection = true; - } elseif ($currentTarget && $inACLSection && preg_match('/^\s+(.+)$/', $line, $matches)) { - $acl = trim($matches[1]); - if (!empty($acl) && !preg_match('/^(Account|I_T nexus|LUN|System)/', $acl)) { - $currentTarget['acls']++; + $section = ''; + } elseif ($currentTarget) { + // Check for LUNs (also simple check for count) + if (preg_match('/^\s+LUN: (\d+)/', $line)) { + $currentTarget['luns']++; + // If we were parsing a session, save it as LUN info usually means we left nexus section or are in LUN section + if ($currentSession) { + $currentTarget['sessions'][] = $currentSession; + $currentSession = null; + } + } + // Section Headers + elseif (preg_match('/^\s+ACL information:/', $line)) { + $section = 'acl'; + if ($currentSession) { $currentTarget['sessions'][] = $currentSession; $currentSession = null; } + } + elseif (preg_match('/^\s+I_T nexus information:/', $line)) { + $section = 'nexus'; + } + elseif (preg_match('/^\s+Account information:/', $line)) { + $section = 'account'; // Ignore or handle if needed + if ($currentSession) { $currentTarget['sessions'][] = $currentSession; $currentSession = null; } + } + + // ACL Section Content + elseif ($section === 'acl' && preg_match('/^\s+(.+)$/', $line, $matches)) { + $content = trim($matches[1]); + // Filter out headers if regex matches them by accident (though section logic prevents this usually) + if (!empty($content) && !preg_match('/^(Account|I_T nexus|LUN|System)/', $content)) { + $currentTarget['acls']++; + } + } + + // I_T Nexus Section Content (Sessions) + elseif ($section === 'nexus') { + if (preg_match('/^\s+I_T nexus: (\d+)/', $line, $matches)) { + // New session found + if ($currentSession) { + $currentTarget['sessions'][] = $currentSession; + } + $currentSession = [ + 'nexus_id' => $matches[1], + 'initiator' => 'Unknown', + 'ip' => 'Unknown' + ]; + } elseif ($currentSession && preg_match('/^\s+Initiator: (.+)$/', $line, $matches)) { + $currentSession['initiator'] = trim($matches[1]); + } elseif ($currentSession && preg_match('/^\s+IP Address: (.+)$/', $line, $matches)) { + $currentSession['ip'] = trim($matches[1]); + } } } } + // Save last items if ($currentTarget) { + if ($currentSession) { + $currentTarget['sessions'][] = $currentSession; + } $targets[] = $currentTarget; } diff --git a/web-ui/index.html b/web-ui/index.html index 81ea183..c28eb57 100644 --- a/web-ui/index.html +++ b/web-ui/index.html @@ -404,6 +404,32 @@
+
+
+

👥 Active Client Sessions

+ +
+
+ + + + + + + + + + + +
TargetInitiator Name (Client)IP Address
+ +
+
+

âž• Create New Target

diff --git a/web-ui/script.js b/web-ui/script.js index ca4832d..0c95e4e 100644 --- a/web-ui/script.js +++ b/web-ui/script.js @@ -1029,6 +1029,38 @@ function loadTargets() { `).join(''); + + // Update Active Sessions Table + const sessionTbody = document.getElementById('iscsi-sessions-body'); + const noSessionsMsg = document.getElementById('no-sessions-msg'); + let hasSessions = false; + + if (sessionTbody) { + let sessionsHtml = ''; + data.targets.forEach(target => { + if (target.sessions && target.sessions.length > 0) { + target.sessions.forEach(session => { + hasSessions = true; + sessionsHtml += ` + + + TID ${target.tid}
+ ${target.name} + + ${session.initiator} + ${session.ip} + + `; + }); + } + }); + + sessionTbody.innerHTML = sessionsHtml; + + if (noSessionsMsg) { + noSessionsMsg.style.display = hasSessions ? 'none' : 'block'; + } + } } } else { showNotification(data.error, 'error');