PHP_AdminTool_Projekt/app/Services/Snmp/SnmpServerStatusService.php

165 lines
6.5 KiB
PHP

<?php
// Strenge Typprüfung für Parameter- und Rückgabetypen aktivieren.
declare(strict_types=1);
namespace App\Services\Snmp;
use RuntimeException;
/**
* Service zur Ermittlung des Serverstatus.
*
* In dieser ersten Version werden noch statische Demo-Daten zurückgegeben.
* Später können hier echte SNMP-Abfragen eingebaut werden, ohne dass sich
* der DashboardController oder die Views ändern müssen.
*/
class SnmpServerStatusService
{
/** @var array<string, mixed> SNMP-spezifische Konfiguration (Host, Community, Timeout, OIDs, etc.) */
private array $config;
/**
* Erwartet den Teilbereich "snmp" aus der allgemeinen Konfiguration (config.php).
*
* @param array<string, mixed> $snmpConfig Konfiguration für die SNMP-Abfragen
*/
public function __construct(array $snmpConfig)
{
// SNMP-Konfiguration in der Instanz speichern.
$this->config = $snmpConfig;
}
/**
* Liefert den aktuellen Serverstatus zurück.
*
* @return array<string, mixed> Assoziatives Array mit Statuswerten (Hostname, CPU%, RAM%, etc.)
*
* @throws RuntimeException wenn die SNMP-Konfiguration unvollständig ist oder Abfragen fehlschlagen
*/
public function getServerStatus(): array
{
// --- 1. Konfiguration auslesen ---
$host = (string)($this->config['host'] ?? '127.0.0.1');
$community = (string)($this->config['community'] ?? '');
$oids = $this->config['oids'] ?? [];
$timeout = (int)($this->config['timeout'] ?? 1) * 1_000_000; // in Mikrosekunden
$retries = (int)($this->config['retries'] ?? 1);
// --- Prüfungen für essentielle Konfig-Werte ---
if ($host === '') {
throw new RuntimeException('SNMP-Konfiguration ist unvollständig (host fehlt).');
}
if ($community === '') {
throw new RuntimeException('SNMP-Konfiguration ist unvollständig (community fehlt).');
}
if (empty($oids)) {
throw new RuntimeException('SNMP-Konfiguration ist unvollständig (oids fehlen).');
}
// Helper-Funktion zum Bereinigen von SNMP-Antworten (z.B. "INTEGER: 123" -> 123)
$cleanSnmpValue = fn($v) => (int)filter_var($v, FILTER_SANITIZE_NUMBER_INT);
// --- 2. Uptime abfragen (war vorher nicht implementiert) ---
$uptimeResult = snmpget($host, $community, $oids['uptime'], $timeout, $retries);
if ($uptimeResult === false) {
throw new RuntimeException("SNMP Uptime GET fehlgeschlagen.");
}
// Uptime (timeticks) in ein lesbares Format umwandeln (optional, hier als String)
// Format ist oft "Timeticks: (12345678) 1 day, 10:17:36.78"
// Wir extrahieren den Teil in Klammern (Hundertstelsekunden)
preg_match('/\((.*?)\)/', $uptimeResult, $matches);
$uptimeTicks = (int)($matches[1] ?? 0);
$uptimeSeconds = $uptimeTicks / 100;
$uptimeFormatted = sprintf(
'%d Tage, %02d:%02d:%02d',
floor($uptimeSeconds / 86400),
floor(($uptimeSeconds % 86400) / 3600),
floor(($uptimeSeconds % 3600) / 60),
$uptimeSeconds % 60
);
// --- 3. CPU ---
$cpuValues = snmpwalk($host, $community, $oids['cpu_table'], $timeout, $retries);
if (!is_array($cpuValues) || empty($cpuValues)) {
throw new RuntimeException("SNMP CPU WALK fehlgeschlagen.");
}
$cpuValues = array_map($cleanSnmpValue, $cpuValues);
$cpuAvg = array_sum($cpuValues) / count($cpuValues);
// --- 4. Memory ---
$memTotalResult = snmpget($host, $community, $oids['mem_size'], $timeout, $retries);
if($memTotalResult === false) {
throw new RuntimeException("SNMP MemTotal GET fehlgeschlagen.");
}
// memTotal in Bytes berechnen (korrigierte Reihenfolge von filter/cast)
$memTotal = $cleanSnmpValue($memTotalResult) * 1024; // KB -> Bytes
// Storage-Tabelle (RAM + Disks)
$descr = snmpwalk($host, $community, $oids['storage_descr'], $timeout, $retries);
$units = snmpwalk($host, $community, $oids['storage_units'], $timeout, $retries);
$size = snmpwalk($host, $community, $oids['storage_size'], $timeout, $retries);
$used = snmpwalk($host, $community, $oids['storage_used'], $timeout, $retries);
if ($descr === false || $units === false || $size === false || $used === false) {
throw new RuntimeException("SNMP Storage WALK fehlgeschlagen.");
}
// Werte bereinigen
// Die SNMP-Antwort enthält "STRING: " und Anführungszeichen, die wir entfernen müssen.
$descr = array_map(fn($v) => trim(str_ireplace('STRING:', '', $v), ' "'), $descr);
$units = array_map($cleanSnmpValue, $units); // Ints
$size = array_map($cleanSnmpValue, $size); // Ints
$used = array_map($cleanSnmpValue, $used); // Ints
// RAM
$ramIndex = array_search("Physical Memory", $descr);
if ($ramIndex === false) {
throw new RuntimeException("Konnte 'Physical Memory' in der SNMP Storage-Tabelle nicht finden.");
}
$ramUsedBytes = $units[$ramIndex] * $used[$ramIndex];
$ramPercent = ($memTotal > 0) ? ($ramUsedBytes / $memTotal) * 100 : 0;
// --- 5. Disk C: ---
$cIndex = false;
foreach ($descr as $index => $description) {
// str_starts_with prüft, ob der String mit C:\ beginnt
if (str_starts_with($description, 'C:\\')) {
$cIndex = $index;
break;
}
}
if ($cIndex === false) {
throw new RuntimeException("Konnte Laufwerk 'C:\' in der SNMP Storage-Tabelle nicht finden.");
}
$cUsed = $units[$cIndex] * $used[$cIndex];
$cTotal = $units[$cIndex] * $size[$cIndex];
$diskCPercent = ($cTotal > 0) ? ($cUsed / $cTotal) * 100 : 0;
// --- 6. Status-Array zusammenbauen ---
$status = [
'hostname' => $host,
'os' => 'Windows Server 2025 Datacenter', // TODO: OS dynamisch abfragen
'uptime' => $uptimeFormatted, // Fehlende Variable hinzugefügt
'cpu_usage' => round($cpuAvg),
'memory_usage' => round($ramPercent),
'disk_usage_c' => round($diskCPercent),
'last_update' => date('d.m.Y H:i:s'),
];
return $status;
}
}