fix: Update vpn_clients speed columns in ServerMonitoring for real-time display
This commit is contained in:
+121
-85
@@ -14,13 +14,13 @@ class ServerMonitoring
|
|||||||
{
|
{
|
||||||
private VpnServer $server;
|
private VpnServer $server;
|
||||||
private array $serverData;
|
private array $serverData;
|
||||||
|
|
||||||
public function __construct(int $serverId)
|
public function __construct(int $serverId)
|
||||||
{
|
{
|
||||||
$this->server = new VpnServer($serverId);
|
$this->server = new VpnServer($serverId);
|
||||||
$this->serverData = $this->server->getData();
|
$this->serverData = $this->server->getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect all server metrics
|
* Collect all server metrics
|
||||||
*/
|
*/
|
||||||
@@ -35,12 +35,12 @@ class ServerMonitoring
|
|||||||
'network_rx_mbps' => $this->getNetworkRxSpeed(),
|
'network_rx_mbps' => $this->getNetworkRxSpeed(),
|
||||||
'network_tx_mbps' => $this->getNetworkTxSpeed(),
|
'network_tx_mbps' => $this->getNetworkTxSpeed(),
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->saveServerMetrics($metrics);
|
$this->saveServerMetrics($metrics);
|
||||||
|
|
||||||
return $metrics;
|
return $metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect client traffic metrics
|
* Collect client traffic metrics
|
||||||
*/
|
*/
|
||||||
@@ -48,10 +48,11 @@ class ServerMonitoring
|
|||||||
{
|
{
|
||||||
$clients = VpnClient::listByServer($this->serverData['id']);
|
$clients = VpnClient::listByServer($this->serverData['id']);
|
||||||
$results = [];
|
$results = [];
|
||||||
|
|
||||||
foreach ($clients as $client) {
|
foreach ($clients as $client) {
|
||||||
if ($client['status'] !== 'active') continue;
|
if ($client['status'] !== 'active')
|
||||||
|
continue;
|
||||||
|
|
||||||
$stats = $this->getClientStats($client);
|
$stats = $this->getClientStats($client);
|
||||||
if ($stats) {
|
if ($stats) {
|
||||||
$this->saveClientMetrics($client['id'], $stats);
|
$this->saveClientMetrics($client['id'], $stats);
|
||||||
@@ -63,10 +64,10 @@ class ServerMonitoring
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get CPU usage percentage
|
* Get CPU usage percentage
|
||||||
*/
|
*/
|
||||||
@@ -74,10 +75,10 @@ class ServerMonitoring
|
|||||||
{
|
{
|
||||||
$cmd = "top -bn1 | grep 'Cpu(s)' | sed 's/.*, *\\([0-9.]*\\)%* id.*/\\1/' | awk '{print 100 - \$1}'";
|
$cmd = "top -bn1 | grep 'Cpu(s)' | sed 's/.*, *\\([0-9.]*\\)%* id.*/\\1/' | awk '{print 100 - \$1}'";
|
||||||
$result = $this->execSSH($cmd);
|
$result = $this->execSSH($cmd);
|
||||||
|
|
||||||
return $result ? (float)trim($result) : null;
|
return $result ? (float) trim($result) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get RAM used in MB
|
* Get RAM used in MB
|
||||||
*/
|
*/
|
||||||
@@ -85,10 +86,10 @@ class ServerMonitoring
|
|||||||
{
|
{
|
||||||
$cmd = "free -m | grep Mem | awk '{print \$3}'";
|
$cmd = "free -m | grep Mem | awk '{print \$3}'";
|
||||||
$result = $this->execSSH($cmd);
|
$result = $this->execSSH($cmd);
|
||||||
|
|
||||||
return $result ? (int)trim($result) : null;
|
return $result ? (int) trim($result) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get total RAM in MB
|
* Get total RAM in MB
|
||||||
*/
|
*/
|
||||||
@@ -96,10 +97,10 @@ class ServerMonitoring
|
|||||||
{
|
{
|
||||||
$cmd = "free -m | grep Mem | awk '{print \$2}'";
|
$cmd = "free -m | grep Mem | awk '{print \$2}'";
|
||||||
$result = $this->execSSH($cmd);
|
$result = $this->execSSH($cmd);
|
||||||
|
|
||||||
return $result ? (int)trim($result) : null;
|
return $result ? (int) trim($result) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get disk used in GB
|
* Get disk used in GB
|
||||||
*/
|
*/
|
||||||
@@ -107,10 +108,10 @@ class ServerMonitoring
|
|||||||
{
|
{
|
||||||
$cmd = "df -BG / | tail -1 | awk '{print \$3}' | sed 's/G//'";
|
$cmd = "df -BG / | tail -1 | awk '{print \$3}' | sed 's/G//'";
|
||||||
$result = $this->execSSH($cmd);
|
$result = $this->execSSH($cmd);
|
||||||
|
|
||||||
return $result ? (float)trim($result) : null;
|
return $result ? (float) trim($result) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get total disk in GB
|
* Get total disk in GB
|
||||||
*/
|
*/
|
||||||
@@ -118,10 +119,10 @@ class ServerMonitoring
|
|||||||
{
|
{
|
||||||
$cmd = "df -BG / | tail -1 | awk '{print \$2}' | sed 's/G//'";
|
$cmd = "df -BG / | tail -1 | awk '{print \$2}' | sed 's/G//'";
|
||||||
$result = $this->execSSH($cmd);
|
$result = $this->execSSH($cmd);
|
||||||
|
|
||||||
return $result ? (float)trim($result) : null;
|
return $result ? (float) trim($result) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get network RX speed in Mbps
|
* Get network RX speed in Mbps
|
||||||
*/
|
*/
|
||||||
@@ -130,22 +131,24 @@ class ServerMonitoring
|
|||||||
// Get bytes received on main interface
|
// Get bytes received on main interface
|
||||||
$cmd = "cat /sys/class/net/\$(ip route | grep default | awk '{print \$5}' | head -1)/statistics/rx_bytes";
|
$cmd = "cat /sys/class/net/\$(ip route | grep default | awk '{print \$5}' | head -1)/statistics/rx_bytes";
|
||||||
$bytes1 = $this->execSSH($cmd);
|
$bytes1 = $this->execSSH($cmd);
|
||||||
|
|
||||||
if (!$bytes1) return null;
|
if (!$bytes1)
|
||||||
|
return null;
|
||||||
|
|
||||||
sleep(1); // Wait 1 second
|
sleep(1); // Wait 1 second
|
||||||
|
|
||||||
$bytes2 = $this->execSSH($cmd);
|
$bytes2 = $this->execSSH($cmd);
|
||||||
|
|
||||||
if (!$bytes2) return null;
|
if (!$bytes2)
|
||||||
|
return null;
|
||||||
|
|
||||||
// Calculate speed in Mbps
|
// Calculate speed in Mbps
|
||||||
$bytesPerSec = (int)$bytes2 - (int)$bytes1;
|
$bytesPerSec = (int) $bytes2 - (int) $bytes1;
|
||||||
$mbps = ($bytesPerSec * 8) / 1000000;
|
$mbps = ($bytesPerSec * 8) / 1000000;
|
||||||
|
|
||||||
return round($mbps, 2);
|
return round($mbps, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get network TX speed in Mbps
|
* Get network TX speed in Mbps
|
||||||
*/
|
*/
|
||||||
@@ -154,40 +157,43 @@ class ServerMonitoring
|
|||||||
// Get bytes transmitted on main interface
|
// Get bytes transmitted on main interface
|
||||||
$cmd = "cat /sys/class/net/\$(ip route | grep default | awk '{print \$5}' | head -1)/statistics/tx_bytes";
|
$cmd = "cat /sys/class/net/\$(ip route | grep default | awk '{print \$5}' | head -1)/statistics/tx_bytes";
|
||||||
$bytes1 = $this->execSSH($cmd);
|
$bytes1 = $this->execSSH($cmd);
|
||||||
|
|
||||||
if (!$bytes1) return null;
|
if (!$bytes1)
|
||||||
|
return null;
|
||||||
|
|
||||||
sleep(1); // Wait 1 second
|
sleep(1); // Wait 1 second
|
||||||
|
|
||||||
$bytes2 = $this->execSSH($cmd);
|
$bytes2 = $this->execSSH($cmd);
|
||||||
|
|
||||||
if (!$bytes2) return null;
|
if (!$bytes2)
|
||||||
|
return null;
|
||||||
|
|
||||||
// Calculate speed in Mbps
|
// Calculate speed in Mbps
|
||||||
$bytesPerSec = (int)$bytes2 - (int)$bytes1;
|
$bytesPerSec = (int) $bytes2 - (int) $bytes1;
|
||||||
$mbps = ($bytesPerSec * 8) / 1000000;
|
$mbps = ($bytesPerSec * 8) / 1000000;
|
||||||
|
|
||||||
return round($mbps, 2);
|
return round($mbps, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get client current stats and calculate speed
|
* Get client current stats and calculate speed
|
||||||
*/
|
*/
|
||||||
private function getClientStats(array $client): ?array
|
private function getClientStats(array $client): ?array
|
||||||
{
|
{
|
||||||
$db = DB::conn();
|
$db = DB::conn();
|
||||||
|
|
||||||
// Get current stats from server
|
// Get current stats from server
|
||||||
$containerName = $this->serverData['container_name'];
|
$containerName = $this->serverData['container_name'];
|
||||||
$publicKey = $client['public_key'];
|
$publicKey = $client['public_key'];
|
||||||
|
|
||||||
$cmd = "docker exec {$containerName} wg show all dump | grep '{$publicKey}' | awk '{print \$6, \$7}'";
|
$cmd = "docker exec {$containerName} wg show all dump | grep '{$publicKey}' | awk '{print \$6, \$7}'";
|
||||||
$result = $this->execSSH($cmd);
|
$result = $this->execSSH($cmd);
|
||||||
|
|
||||||
if (!$result) return null;
|
if (!$result)
|
||||||
|
return null;
|
||||||
|
|
||||||
list($bytesReceived, $bytesSent) = explode(' ', trim($result));
|
list($bytesReceived, $bytesSent) = explode(' ', trim($result));
|
||||||
|
|
||||||
// Get previous metrics (30 seconds ago)
|
// Get previous metrics (30 seconds ago)
|
||||||
$stmt = $db->prepare("
|
$stmt = $db->prepare("
|
||||||
SELECT bytes_sent, bytes_received, collected_at
|
SELECT bytes_sent, bytes_received, collected_at
|
||||||
@@ -198,43 +204,43 @@ class ServerMonitoring
|
|||||||
");
|
");
|
||||||
$stmt->execute([$client['id']]);
|
$stmt->execute([$client['id']]);
|
||||||
$previous = $stmt->fetch(PDO::FETCH_ASSOC);
|
$previous = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
$speedUp = 0;
|
$speedUp = 0;
|
||||||
$speedDown = 0;
|
$speedDown = 0;
|
||||||
|
|
||||||
if ($previous) {
|
if ($previous) {
|
||||||
$timeDiff = time() - strtotime($previous['collected_at']);
|
$timeDiff = time() - strtotime($previous['collected_at']);
|
||||||
if ($timeDiff > 0) {
|
if ($timeDiff > 0) {
|
||||||
// Calculate speed in Kbps
|
// Calculate speed in Kbps
|
||||||
$bytesDiffSent = (int)$bytesSent - (int)$previous['bytes_sent'];
|
$bytesDiffSent = (int) $bytesSent - (int) $previous['bytes_sent'];
|
||||||
$bytesDiffReceived = (int)$bytesReceived - (int)$previous['bytes_received'];
|
$bytesDiffReceived = (int) $bytesReceived - (int) $previous['bytes_received'];
|
||||||
|
|
||||||
$speedUp = round(($bytesDiffSent * 8) / $timeDiff / 1000, 2);
|
$speedUp = round(($bytesDiffSent * 8) / $timeDiff / 1000, 2);
|
||||||
$speedDown = round(($bytesDiffReceived * 8) / $timeDiff / 1000, 2);
|
$speedDown = round(($bytesDiffReceived * 8) / $timeDiff / 1000, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'bytes_sent' => (int)$bytesSent,
|
'bytes_sent' => (int) $bytesSent,
|
||||||
'bytes_received' => (int)$bytesReceived,
|
'bytes_received' => (int) $bytesReceived,
|
||||||
'speed_up_kbps' => $speedUp,
|
'speed_up_kbps' => $speedUp,
|
||||||
'speed_down_kbps' => $speedDown,
|
'speed_down_kbps' => $speedDown,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save server metrics to database
|
* Save server metrics to database
|
||||||
*/
|
*/
|
||||||
private function saveServerMetrics(array $metrics): void
|
private function saveServerMetrics(array $metrics): void
|
||||||
{
|
{
|
||||||
$db = DB::conn();
|
$db = DB::conn();
|
||||||
|
|
||||||
$stmt = $db->prepare("
|
$stmt = $db->prepare("
|
||||||
INSERT INTO server_metrics
|
INSERT INTO server_metrics
|
||||||
(server_id, cpu_percent, ram_used_mb, ram_total_mb, disk_used_gb, disk_total_gb, network_rx_mbps, network_tx_mbps)
|
(server_id, cpu_percent, ram_used_mb, ram_total_mb, disk_used_gb, disk_total_gb, network_rx_mbps, network_tx_mbps)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
");
|
");
|
||||||
|
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
$this->serverData['id'],
|
$this->serverData['id'],
|
||||||
$metrics['cpu_percent'],
|
$metrics['cpu_percent'],
|
||||||
@@ -246,20 +252,20 @@ class ServerMonitoring
|
|||||||
$metrics['network_tx_mbps'],
|
$metrics['network_tx_mbps'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save client metrics to database
|
* Save client metrics to database
|
||||||
*/
|
*/
|
||||||
private function saveClientMetrics(int $clientId, array $stats): void
|
private function saveClientMetrics(int $clientId, array $stats): void
|
||||||
{
|
{
|
||||||
$db = DB::conn();
|
$db = DB::conn();
|
||||||
|
|
||||||
$stmt = $db->prepare("
|
$stmt = $db->prepare("
|
||||||
INSERT INTO client_metrics
|
INSERT INTO client_metrics
|
||||||
(client_id, bytes_sent, bytes_received, speed_up_kbps, speed_down_kbps)
|
(client_id, bytes_sent, bytes_received, speed_up_kbps, speed_down_kbps)
|
||||||
VALUES (?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?)
|
||||||
");
|
");
|
||||||
|
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
$clientId,
|
$clientId,
|
||||||
$stats['bytes_sent'],
|
$stats['bytes_sent'],
|
||||||
@@ -267,24 +273,54 @@ class ServerMonitoring
|
|||||||
$stats['speed_up_kbps'],
|
$stats['speed_up_kbps'],
|
||||||
$stats['speed_down_kbps'],
|
$stats['speed_down_kbps'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Update last_handshake in vpn_clients table
|
// Update vpn_clients table with latest stats
|
||||||
$stmt = $db->prepare("
|
$stmt = $db->prepare("
|
||||||
UPDATE vpn_clients
|
UPDATE vpn_clients
|
||||||
SET last_handshake = NOW()
|
SET bytes_sent = ?, bytes_received = ?, speed_up = ?, speed_down = ?, current_speed = ?, last_handshake = NOW(), last_sync_at = NOW()
|
||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
");
|
");
|
||||||
|
|
||||||
$stmt->execute([$clientId]);
|
$currentSpeed = $stats['speed_up_kbps'] + $stats['speed_down_kbps']; // Total speed in Kbps? Or bytes/s?
|
||||||
|
// Note: speed_up_kbps is in Kbps (kilobits?).
|
||||||
|
// VpnClient stores speed in Bytes/s (based on my previous edit: bytesDiff/timeDiff).
|
||||||
|
// ServerMonitoring calculates: round(($bytesDiffSent * 8) / $timeDiff / 1000, 2) -> Kbps
|
||||||
|
|
||||||
|
// Wait! VpnClient implementation I did:
|
||||||
|
// $speedUp = (int) ($sentDiff / $timeDiff); // Bytes per second
|
||||||
|
|
||||||
|
// ServerMonitoring implementation:
|
||||||
|
// $speedUp = round(($bytesDiffSent * 8) / $timeDiff / 1000, 2); // Kilobits per second
|
||||||
|
|
||||||
|
// I need to be consistent.
|
||||||
|
// Frontend expects KB/s (KiloBYTES).
|
||||||
|
// VpnClient stores BYTES per second. Twig does `speed / 1024` -> KB/s.
|
||||||
|
|
||||||
|
// So I should convert ServerMonitoring stats to Bytes/s before saving to vpn_clients.
|
||||||
|
// ServerMonitoring $stats['speed_up_kbps'] is Kbps.
|
||||||
|
// Bytes/s = Kbps * 1000 / 8.
|
||||||
|
|
||||||
|
$speedUpBytes = (int) ($stats['speed_up_kbps'] * 1000 / 8);
|
||||||
|
$speedDownBytes = (int) ($stats['speed_down_kbps'] * 1000 / 8);
|
||||||
|
$totalSpeedBytes = $speedUpBytes + $speedDownBytes;
|
||||||
|
|
||||||
|
$stmt->execute([
|
||||||
|
$stats['bytes_sent'],
|
||||||
|
$stats['bytes_received'],
|
||||||
|
$speedUpBytes,
|
||||||
|
$speedDownBytes,
|
||||||
|
$totalSpeedBytes,
|
||||||
|
$clientId
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get server metrics for last 24 hours
|
* Get server metrics for last 24 hours
|
||||||
*/
|
*/
|
||||||
public static function getServerMetrics(int $serverId, int $hours = 24): array
|
public static function getServerMetrics(int $serverId, int $hours = 24): array
|
||||||
{
|
{
|
||||||
$db = DB::conn();
|
$db = DB::conn();
|
||||||
|
|
||||||
$stmt = $db->prepare("
|
$stmt = $db->prepare("
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM server_metrics
|
FROM server_metrics
|
||||||
@@ -292,19 +328,19 @@ class ServerMonitoring
|
|||||||
AND collected_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)
|
AND collected_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)
|
||||||
ORDER BY collected_at ASC
|
ORDER BY collected_at ASC
|
||||||
");
|
");
|
||||||
|
|
||||||
$stmt->execute([$serverId, $hours]);
|
$stmt->execute([$serverId, $hours]);
|
||||||
|
|
||||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get client metrics for last 24 hours
|
* Get client metrics for last 24 hours
|
||||||
*/
|
*/
|
||||||
public static function getClientMetrics(int $clientId, int $hours = 24): array
|
public static function getClientMetrics(int $clientId, int $hours = 24): array
|
||||||
{
|
{
|
||||||
$db = DB::conn();
|
$db = DB::conn();
|
||||||
|
|
||||||
$stmt = $db->prepare("
|
$stmt = $db->prepare("
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM client_metrics
|
FROM client_metrics
|
||||||
@@ -312,26 +348,26 @@ class ServerMonitoring
|
|||||||
AND collected_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)
|
AND collected_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)
|
||||||
ORDER BY collected_at ASC
|
ORDER BY collected_at ASC
|
||||||
");
|
");
|
||||||
|
|
||||||
$stmt->execute([$clientId, $hours]);
|
$stmt->execute([$clientId, $hours]);
|
||||||
|
|
||||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean old metrics (older than 24 hours)
|
* Clean old metrics (older than 24 hours)
|
||||||
*/
|
*/
|
||||||
public static function cleanOldMetrics(): void
|
public static function cleanOldMetrics(): void
|
||||||
{
|
{
|
||||||
$db = DB::conn();
|
$db = DB::conn();
|
||||||
|
|
||||||
// Clean server metrics
|
// Clean server metrics
|
||||||
$db->exec("DELETE FROM server_metrics WHERE collected_at < DATE_SUB(NOW(), INTERVAL 24 HOUR)");
|
$db->exec("DELETE FROM server_metrics WHERE collected_at < DATE_SUB(NOW(), INTERVAL 24 HOUR)");
|
||||||
|
|
||||||
// Clean client metrics
|
// Clean client metrics
|
||||||
$db->exec("DELETE FROM client_metrics WHERE collected_at < DATE_SUB(NOW(), INTERVAL 24 HOUR)");
|
$db->exec("DELETE FROM client_metrics WHERE collected_at < DATE_SUB(NOW(), INTERVAL 24 HOUR)");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute SSH command on server
|
* Execute SSH command on server
|
||||||
*/
|
*/
|
||||||
@@ -341,7 +377,7 @@ class ServerMonitoring
|
|||||||
$port = $this->serverData['port'];
|
$port = $this->serverData['port'];
|
||||||
$username = $this->serverData['username'];
|
$username = $this->serverData['username'];
|
||||||
$password = $this->serverData['password'];
|
$password = $this->serverData['password'];
|
||||||
|
|
||||||
$sshCmd = sprintf(
|
$sshCmd = sprintf(
|
||||||
'sshpass -p %s ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p %d %s@%s %s 2>/dev/null',
|
'sshpass -p %s ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p %d %s@%s %s 2>/dev/null',
|
||||||
escapeshellarg($password),
|
escapeshellarg($password),
|
||||||
@@ -350,9 +386,9 @@ class ServerMonitoring
|
|||||||
escapeshellarg($host),
|
escapeshellarg($host),
|
||||||
escapeshellarg($cmd)
|
escapeshellarg($cmd)
|
||||||
);
|
);
|
||||||
|
|
||||||
$output = shell_exec($sshCmd);
|
$output = shell_exec($sshCmd);
|
||||||
|
|
||||||
return $output ?: null;
|
return $output ?: null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user