From a639cd68e37995364cdb4cf8903982ff0446bc1f Mon Sep 17 00:00:00 2001 From: GraegelTh <165781231+GraegelTh@users.noreply.github.com> Date: Thu, 11 Dec 2025 15:03:12 +0100 Subject: [PATCH] Update der Api mit Snmpwalk Umstellung des Snmpwalk damit die Werte auf dem Liveserver funktionieren --- public/api/snmp_status.php | 247 +++++++++++++++---------------------- 1 file changed, 101 insertions(+), 146 deletions(-) diff --git a/public/api/snmp_status.php b/public/api/snmp_status.php index 4d26b23..5045a25 100644 --- a/public/api/snmp_status.php +++ b/public/api/snmp_status.php @@ -6,16 +6,12 @@ declare(strict_types=1); * * Nur authentifizierte Admins dürfen auf diesen Endpunkt zugreifen. * Wird alle 5s vom JavaScript im Dashboard aufgerufen. - * - * Sicherheit: Session-Validierung + Admin-Rollenprüfung. - * Performance: Datei-basiertes Caching (10 Sekunden) um SNMP-Last zu reduzieren. - * Logging: Alle Fehler und wichtigen Daten werden in `public/logs/snmp_api.log` geschrieben. */ session_start(); // === Authentifizierung + Autorisierung === -$sessionKeyUser = 'admin_user'; // aus config +$sessionKeyUser = 'admin_user'; if (!isset($_SESSION[$sessionKeyUser])) { http_response_code(401); header('Content-Type: application/json; charset=utf-8'); @@ -40,19 +36,15 @@ function log_msg(string $msg): void { function rotate_log_if_needed(): void { global $logFile; - $maxSize = 500 * 1024; // 500 KB (anpassbar) - + $maxSize = 500 * 1024; // 500 KB if (file_exists($logFile) && filesize($logFile) > $maxSize) { - $backupFile = $logFile . '.old'; - @rename($logFile, $backupFile); - log_msg('=== Log rotiert (alte Datei: .old) ==='); + @rename($logFile, $logFile . '.old'); + log_msg('=== Log rotiert ==='); } } - $configPath = __DIR__ . '/../../config/config.php'; if (!is_readable($configPath)) { - log_msg('ERROR: config.php nicht lesbar'); echo json_encode(['error' => 'config_not_found']); exit; } @@ -62,10 +54,7 @@ $snmp = $config['snmp'] ?? []; rotate_log_if_needed(); -log_msg('--- SNMP-Abfrage gestartet ---'); - // === Cache-Logik (Datei) === -// Cache-Datei wird nach 10 Sekunden als ungültig betrachtet $cacheDir = sys_get_temp_dir(); $cacheFile = $cacheDir . DIRECTORY_SEPARATOR . 'snmp_status_cache.json'; $cacheTTL = 10; // Sekunden @@ -73,53 +62,54 @@ $cacheTTL = 10; // Sekunden if (file_exists($cacheFile)) { $cacheAge = time() - filemtime($cacheFile); if ($cacheAge < $cacheTTL) { - // Cache ist noch gültig $cached = file_get_contents($cacheFile); if ($cached !== false) { - log_msg("Cache HIT (Alter: {$cacheAge}s)"); echo $cached; exit; } - } else { - log_msg("Cache abgelaufen (Alter: {$cacheAge}s), neue Abfrage"); } } +// === SNMP Setup === $host = $snmp['host'] ?? '127.0.0.1'; $community = $snmp['community'] ?? 'public'; -$timeout = (int)($snmp['timeout'] ?? 2); + +// Timeout von Sekunden in Mikrosekunden umrechnen (wichtig für PHP snmp Funktionen) +$timeoutSec = (int)($snmp['timeout'] ?? 2); +$timeoutMicro = $timeoutSec * 1_000_000; $retries = (int)($snmp['retries'] ?? 1); -log_msg("SNMP-Host: $host, Community: $community, Timeout: {$timeout}s"); - if (!function_exists('snmpget')) { - log_msg('ERROR: SNMP-Erweiterung nicht installiert'); echo json_encode(['error' => 'snmp_extension_missing']); exit; } -snmp_set_quick_print(true); +// Grundeinstellungen snmp_set_oid_output_format(SNMP_OID_OUTPUT_NUMERIC); -// Hilfsfunktion: sichere snmpget-Rückgabe (numeric oder null) -function sget(string $host, string $community, string $oid, int $timeout, int $retries) -{ +// Hilfsfunktion: sichere snmpget-Rückgabe +function sget(string $host, string $community, string $oid, int $timeout, int $retries) { $v = @snmpget($host, $community, $oid, $timeout, $retries); if ($v === false || $v === null) return null; return is_string($v) ? trim($v) : $v; } -// System-Uptime (TimeTicks) +// --- 1. Uptime --- +// Wir deaktivieren quick_print, damit wir das Format "Timeticks: (12345) 1 day..." erhalten. +// Nur so können wir die echten Ticks in der Klammer zuverlässig parsen. +snmp_set_quick_print(false); + $uptimeOid = $snmp['oids']['uptime'] ?? '1.3.6.1.2.1.1.3.0'; -$upticksRaw = @sget($host, $community, $uptimeOid, $timeout, $retries); -$upticks = $upticksRaw !== null ? (int)preg_replace('/[^0-9]/', '', (string)$upticksRaw) : null; +$uptimeRaw = @sget($host, $community, $uptimeOid, $timeoutMicro, $retries); +$upticks = null; -log_msg("Uptime OID: $uptimeOid, Raw: " . ($upticksRaw ?? 'null')); +// Regex sucht nach Zahl in Klammern: (12345678) +if ($uptimeRaw && preg_match('/\((.*?)\)/', (string)$uptimeRaw, $matches)) { + $upticks = (int)$matches[1]; +} -function format_uptime(?int $ticks): ?string -{ +function format_uptime(?int $ticks): ?string { if ($ticks === null) return null; - // ticks sind Hundertstel-Sekunden $seconds = (int)floor($ticks / 100); $days = intdiv($seconds, 86400); $seconds -= $days * 86400; @@ -127,166 +117,131 @@ function format_uptime(?int $ticks): ?string $seconds -= $hours * 3600; $minutes = intdiv($seconds, 60); $seconds -= $minutes * 60; + $parts = []; if ($days) $parts[] = $days . ' Tage'; - if ($hours) $parts[] = $hours . ' Std'; - if ($minutes) $parts[] = $minutes . ' Min'; - $parts[] = $seconds . ' Sek'; - return implode(' ', $parts); + $parts[] = sprintf('%02d:%02d:%02d', $hours, $minutes, $seconds); + return implode(', ', $parts); } $uptimeStr = format_uptime($upticks); -// CPU: ersten Eintrag aus der CPU-Tabelle lesen +// Für den Rest aktivieren wir quick_print wieder, um "saubere" Werte zu bekommen +snmp_set_quick_print(true); + + +// --- 2. CPU (Walk über alle Kerne) --- $cpuTable = $snmp['oids']['cpu_table'] ?? '1.3.6.1.2.1.25.3.3.1.2'; -$cpuOid = $cpuTable . '.1'; -$cpuRaw = @sget($host, $community, $cpuOid, $timeout, $retries); -$cpu = $cpuRaw !== null ? (float)preg_replace('/[^0-9.]/', '', (string)$cpuRaw) : null; +$cpuValues = @snmpwalk($host, $community, $cpuTable, $timeoutMicro, $retries); -log_msg("CPU OID: $cpuOid, Raw: " . ($cpuRaw ?? 'null') . ", Parsed: " . ($cpu ?? 'null')); +$cpuUsage = 0; +if (is_array($cpuValues) && count($cpuValues) > 0) { + $totalLoad = 0; + $coreCount = 0; + foreach ($cpuValues as $val) { + $v = (int)filter_var($val, FILTER_SANITIZE_NUMBER_INT); + $totalLoad += $v; + $coreCount++; + } + if ($coreCount > 0) { + $cpuUsage = round($totalLoad / $coreCount, 2); + } +} -// Storage-Tabellen: Versuche, C: (Windows) oder Root ("/") zu finden + +// --- 3. Storage (Disk & RAM) --- $descrOid = $snmp['oids']['storage_descr'] ?? '1.3.6.1.2.1.25.2.3.1.3'; $unitsOid = $snmp['oids']['storage_units'] ?? '1.3.6.1.2.1.25.2.3.1.4'; -$sizeOid = $snmp['oids']['storage_size'] ?? '1.3.6.1.2.1.25.2.3.1.5'; -$usedOid = $snmp['oids']['storage_used'] ?? '1.3.6.1.2.1.25.2.3.1.6'; +$sizeOid = $snmp['oids']['storage_size'] ?? '1.3.6.1.2.1.25.2.3.1.5'; +$usedOid = $snmp['oids']['storage_used'] ?? '1.3.6.1.2.1.25.2.3.1.6'; -log_msg("Starte Storage-Walk - Descr: $descrOid, Units: $unitsOid, Size: $sizeOid, Used: $usedOid"); - -$descrWalk = @snmprealwalk($host, $community, $descrOid); -$unitsWalk = @snmprealwalk($host, $community, $unitsOid); -$sizeWalk = @snmprealwalk($host, $community, $sizeOid); -$usedWalk = @snmprealwalk($host, $community, $usedOid); - -if (!is_array($descrWalk)) { - log_msg('WARNING: snmprealwalk für Beschreibungen fehlgeschlagen'); - $descrWalk = []; -} -if (!is_array($unitsWalk)) { - log_msg('WARNING: snmprealwalk für Units fehlgeschlagen'); - $unitsWalk = []; -} -if (!is_array($sizeWalk)) { - log_msg('WARNING: snmprealwalk für Größe fehlgeschlagen'); - $sizeWalk = []; -} -if (!is_array($usedWalk)) { - log_msg('WARNING: snmprealwalk für Used fehlgeschlagen'); - $usedWalk = []; -} - -log_msg('Storage-Einträge gefunden: ' . count($descrWalk)); +$descrWalk = @snmprealwalk($host, $community, $descrOid, $timeoutMicro, $retries); +$unitsWalk = @snmprealwalk($host, $community, $unitsOid, $timeoutMicro, $retries); +$sizeWalk = @snmprealwalk($host, $community, $sizeOid, $timeoutMicro, $retries); +$usedWalk = @snmprealwalk($host, $community, $usedOid, $timeoutMicro, $retries); $diskPercent = null; $memPercent = null; -$storageEntries = []; +$storageEntries = []; // Fallback-Liste -// Sammeln aller Storage-Einträge für Debug-Logging -if (is_array($descrWalk) && count($descrWalk) > 0) { +if (is_array($descrWalk)) { foreach ($descrWalk as $descrOidFull => $descrRaw) { - // Index extrahieren if (!preg_match('/\.(\d+)$/', $descrOidFull, $m)) continue; $idx = $m[1]; - $descr = trim((string)$descrRaw, ' "'); - // Suche nach passenden Einträgen in anderen Walks mit gleichem Index - $units = null; - $size = null; - $used = null; + // Bereinigen + $descr = trim(str_ireplace('STRING:', '', (string)$descrRaw), ' "'); - foreach ($unitsWalk as $oid => $val) { - if (preg_match('/\.(\d+)$/', $oid, $m2) && $m2[1] === $idx) { - $units = (int)preg_replace('/[^0-9]/', '', (string)$val); - break; + // Helper zum Finden der Werte + $findVal = function($walkArr, $idx) { + if(!is_array($walkArr)) return null; + foreach ($walkArr as $oid => $val) { + if (preg_match('/\.(\d+)$/', $oid, $m2) && $m2[1] === $idx) { + return (int)filter_var($val, FILTER_SANITIZE_NUMBER_INT); + } } - } - - foreach ($sizeWalk as $oid => $val) { - if (preg_match('/\.(\d+)$/', $oid, $m2) && $m2[1] === $idx) { - $size = (int)preg_replace('/[^0-9]/', '', (string)$val); - break; - } - } - - foreach ($usedWalk as $oid => $val) { - if (preg_match('/\.(\d+)$/', $oid, $m2) && $m2[1] === $idx) { - $used = (int)preg_replace('/[^0-9]/', '', (string)$val); - break; - } - } + return null; + }; - log_msg("Storage[$idx]: Desc='$descr', Size=$size, Used=$used, Units=$units"); + $units = $findVal($unitsWalk, $idx); + $size = $findVal($sizeWalk, $idx); + $used = $findVal($usedWalk, $idx); - if ($size === null || $units === null || $used === null || $size === 0 || $units === 0) { - log_msg("Storage[$idx]: SKIP (fehlende oder ungültige Daten)"); - continue; - } + if ($size === null || $units === null || $used === null || $size === 0) continue; $totalBytes = $size * $units; $usedBytes = $used * $units; - $percent = $totalBytes > 0 ? ($usedBytes / $totalBytes) * 100 : 0; + $percent = ($totalBytes > 0) ? ($usedBytes / $totalBytes) * 100 : 0; - $storageEntries[] = [ - 'idx' => $idx, - 'descr' => $descr, - 'percent' => $percent, - 'totalGB' => $totalBytes / (1024 ** 3), - 'usedGB' => $usedBytes / (1024 ** 3), - ]; + $storageEntries[] = ['idx' => $idx, 'descr' => $descr, 'percent' => $percent, 'totalGB' => $totalBytes / (1024**3)]; - // Heuristik 1: Suche nach C: oder Root $lower = strtolower($descr); - if ($diskPercent === null && (strpos($lower, 'c:') !== false || strpos($lower, 'c:\\') !== false || strpos($lower, 'c:/') !== false || $lower === '/' || strpos($lower, 'root') !== false)) { - $diskPercent = $percent; - log_msg("Datenträger erkannt (Index $idx): $descr → $percent%"); + + // DISK C: oder Root + if ($diskPercent === null) { + if (str_starts_with($lower, 'c:') || str_starts_with($lower, 'c:\\') || $lower === '/' || str_contains($lower, 'root')) { + $diskPercent = $percent; + } } - // Heuristik 2: Suche nach Physical Memory - if ($memPercent === null && ((strpos($lower, 'physical') !== false && strpos($lower, 'memory') !== false) || strpos($lower, 'ram') !== false || strpos($lower, 'physical memory') !== false)) { - $memPercent = $percent; - log_msg("Speicher erkannt (Index $idx): $descr → $percent%"); + // RAM + if ($memPercent === null) { + if (str_contains($lower, 'physical memory') || str_contains($lower, 'ram')) { + $memPercent = $percent; + } } } } -// Fallback 1: Wenn keine Disk gefunden, nimm den größten Storage-Eintrag > 1GB +// Fallback Disk: Größter Speicher > 5GB if ($diskPercent === null && count($storageEntries) > 0) { - log_msg('Fallback: Suche Disk (größter Eintrag > 1GB)'); usort($storageEntries, fn($a, $b) => $b['totalGB'] <=> $a['totalGB']); - foreach ($storageEntries as $entry) { - if ($entry['totalGB'] > 1) { + foreach($storageEntries as $entry) { + if ($entry['totalGB'] > 5) { $diskPercent = $entry['percent']; - log_msg('Fallback Disk gefunden (Index ' . $entry['idx'] . '): ' . $entry['descr'] . ' → ' . $diskPercent . '%'); break; } } } -// Fallback 2: Wenn kein Speicher gefunden, nimm den kleinsten Eintrag (meist Physical Memory) -if ($memPercent === null && count($storageEntries) > 0) { - log_msg('Fallback: Suche Memory (kleinster Eintrag)'); - usort($storageEntries, fn($a, $b) => $a['totalGB'] <=> $b['totalGB']); - $memPercent = $storageEntries[0]['percent']; - log_msg('Fallback Memory gefunden (Index ' . $storageEntries[0]['idx'] . '): ' . $storageEntries[0]['descr'] . ' → ' . $memPercent . '%'); -} +// --- 4. Hostname --- +$hostnameOid = '1.3.6.1.2.1.1.5.0'; +$hostname = @sget($host, $community, $hostnameOid, $timeoutMicro, $retries); +if($hostname) $hostname = trim(str_ireplace('STRING:', '', $hostname), ' "'); +// --- Ergebnis --- $result = [ - 'hostname' => @sget($host, $community, '1.3.6.1.2.1.1.5.0', $timeout, $retries) ?? null, - 'uptime' => $uptimeStr, - 'upticks' => $upticks, - 'cpu_usage' => $cpu, - 'memory_usage' => $memPercent !== null ? round($memPercent, 2) : null, - 'disk_usage_c' => $diskPercent !== null ? round($diskPercent, 2) : null, - 'last_update' => time(), + 'hostname' => $hostname ?? 'n/a', + 'uptime' => $uptimeStr, + 'upticks' => $upticks, + 'cpu_usage' => $cpuUsage, + 'memory_usage' => $memPercent !== null ? round($memPercent, 2) : 0, + 'disk_usage_c' => $diskPercent !== null ? round($diskPercent, 2) : 0, + 'last_update' => time(), ]; -log_msg('RESULT: CPU=' . $result['cpu_usage'] . ', Mem=' . $result['memory_usage'] . ', Disk=' . $result['disk_usage_c']); +log_msg('RESULT: UptimeRaw='.($uptimeRaw??'null').' CPU=' . $result['cpu_usage'] . ' Mem=' . $result['memory_usage']); $resultJson = json_encode($result); - -// === Cache schreiben === @file_put_contents($cacheFile, $resultJson); -log_msg('Cache geschrieben, TTL: ' . $cacheTTL . 's'); - -echo $resultJson; -exit; +echo $resultJson; \ No newline at end of file -- 2.45.2