Wer einen Server aus der Ping-Ueberwachung nehmen wollte, musste sich bisher auf den Rechner mit check_fds.sh einloggen und die hosts.conf editieren - und die Kachel blieb danach trotzdem im System-Monitor stehen, weil ein einmal gemeldetes System nirgends wieder geloescht werden konnte. Die Liste steht jetzt in der Datenbank und ist ueber ein Zahnrad neben der Ueberschrift "Systeme" pflegbar, analog zum Zahnrad am PO-Platz (Issue #27). Jeder Host gehoert dem OPs-Agenten (Issue #66), der ihn anpingt: API und Ping-Check laufen nicht zwangslaeufig auf demselben Rechner, die API kann die hosts.conf also nicht selbst schreiben. check_fds.sh holt sie stattdessen bei jedem Lauf mit seinem Agenten-Key ab und schreibt daraus die lokale Datei; ist die API nicht erreichbar, pingt es unveraendert nach der zuletzt geschriebenen Liste weiter. Beim ersten Lauf laedt der Check seine bestehende hosts.conf hoch, damit die bereits ueberwachten Server nicht von Hand nachgetragen werden muessen. Dass die API das nur einmal annimmt (Marker in host_config_imports) und "noch nie konfiguriert" per 204 von "im Frontend geleert" per 200 unterscheidet, ist noetig, damit ein Ping-Rechner mit alter lokaler Datei die dort geloeschten Hosts nicht wieder anlegt. Loeschen raeumt mit auf: Ein entfernter Host nimmt seine Kachel mit - beide Ids, denn der status.txt-Import benennt ihn nach dem Hostnamen, die REST-Meldung nach dessen Slug. Kacheln ohne zugehoerigen Host (Selbstmelder oder Reste laengst entfernter Hosts) stehen im selben Dialog unter "Weitere Kacheln im System-Monitor" und sind dort einzeln loeschbar - dafuer gibt es neu DELETE /api/v1/systems/{id}. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015vNjbfr8M3HSfWMee92GTE
136 lines
5.1 KiB
PHP
136 lines
5.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Monitor\Api\Controller;
|
|
|
|
use InvalidArgumentException;
|
|
use Monitor\Api\Http\Request;
|
|
use Monitor\Api\Http\Response;
|
|
use Monitor\Api\Repository\HostConfigRepository;
|
|
use Monitor\Api\Repository\SystemRepository;
|
|
|
|
/**
|
|
* Issue #70: Die Hosts der Ping-Ueberwachung, bisher nur in der hosts.conf neben
|
|
* check_fds.sh. Zwei Zugriffsarten mit unterschiedlicher Berechtigung:
|
|
*
|
|
* - Pflege aus dem Frontend (Zahnrad am Wandmonitor "Systeme") - schreibend hinter dem
|
|
* Admin-Key wie die Projekte-Verwaltung (Issue #27), kein Bot-Vorgang.
|
|
* - Abruf durch den Ping-Check selbst - hinter dem Agenten-Key, weil erst der sagt,
|
|
* welcher Ping-Rechner fragt und damit welche Liste er bekommt.
|
|
*/
|
|
final class HostConfigController
|
|
{
|
|
public function __construct(
|
|
private readonly HostConfigRepository $hosts,
|
|
private readonly SystemRepository $systems,
|
|
) {
|
|
}
|
|
|
|
public function index(Request $request): Response
|
|
{
|
|
// system_ids kommen mit, damit das Frontend eine Kachel im System-Monitor sicher
|
|
// ihrem Host zuordnen kann, ohne die Id-Ableitung (Hostname vs. Slug) nachzubauen.
|
|
$hosts = array_map(
|
|
fn (array $host) => $host + ['system_ids' => SystemRepository::idsForHostname($host['hostname'])],
|
|
$this->hosts->all()
|
|
);
|
|
|
|
return Response::json(['hosts' => $hosts]);
|
|
}
|
|
|
|
public function create(Request $request): Response
|
|
{
|
|
try {
|
|
$host = $this->hosts->create($request->body);
|
|
} catch (InvalidArgumentException $e) {
|
|
return Response::error($e->getMessage(), 422);
|
|
}
|
|
|
|
return Response::json(['host' => $host], 201);
|
|
}
|
|
|
|
/** @param array<string, string> $params */
|
|
public function update(Request $request, array $params): Response
|
|
{
|
|
$previous = $this->hosts->find((int) $params['id']);
|
|
|
|
try {
|
|
$host = $this->hosts->update((int) $params['id'], $request->body);
|
|
} catch (InvalidArgumentException $e) {
|
|
return Response::error($e->getMessage(), 422);
|
|
}
|
|
|
|
// Ein umbenannter Host meldet sich unter neuer System-Id; die alte Kachel haette
|
|
// sonst niemanden mehr, der sie aktualisiert, und bliebe fuer immer stehen.
|
|
if ($previous !== null && $previous['hostname'] !== $host['hostname']) {
|
|
$this->systems->delete(SystemRepository::idsForHostname($previous['hostname']));
|
|
}
|
|
|
|
return Response::json(['host' => $host]);
|
|
}
|
|
|
|
/**
|
|
* Entfernt den Host aus der Ueberwachung und raeumt seine Kachel im System-Monitor
|
|
* gleich mit ab - analog zu Issue #43, wo ein entferntes Projekt auch seine Tickets
|
|
* mitnimmt. Ohne das bliebe die (womoeglich rote) Kachel stehen, obwohl niemand den
|
|
* Host mehr prueft.
|
|
*
|
|
* @param array<string, string> $params
|
|
*/
|
|
public function delete(Request $request, array $params): Response
|
|
{
|
|
$host = $this->hosts->delete((int) $params['id']);
|
|
$this->systems->delete(SystemRepository::idsForHostname($host['hostname']));
|
|
|
|
return Response::json(['deleted' => true]);
|
|
}
|
|
|
|
/**
|
|
* Die hosts.conf dieses Ping-Rechners als Klartext - check_fds.sh schreibt den Body
|
|
* direkt in seine lokale Datei.
|
|
*
|
|
* 204 statt 200 heisst "fuer dich ist hier noch nichts konfiguriert": dann soll der
|
|
* Ping-Rechner seine bestehende Datei abgeben (import()), statt sie zu ueberschreiben.
|
|
* Ohne diese Unterscheidung waere eine leere Antwort zweideutig - eine im Frontend
|
|
* geleerte Liste saehe aus wie eine unbenutzte, und der Check pingte munter nach seiner
|
|
* alten lokalen Datei weiter.
|
|
*
|
|
* @param array<string, string> $params
|
|
* @param array<string, mixed> $agent - der ueber X-Api-Key authentifizierte Agent.
|
|
*/
|
|
public function conf(Request $request, array $params, array $agent): Response
|
|
{
|
|
if (($agent['role'] ?? '') !== 'ops') {
|
|
return Response::error('Nur Agenten der Rolle "ops" haben eine hosts.conf.', 403);
|
|
}
|
|
|
|
$agentId = (int) $agent['id'];
|
|
if (!$this->hosts->hasImported($agentId) && $this->hosts->allForAgent($agentId) === []) {
|
|
return Response::text('', 204);
|
|
}
|
|
|
|
return Response::text($this->hosts->renderHostsConf($agentId));
|
|
}
|
|
|
|
/**
|
|
* Erstbefuellung: check_fds.sh schickt seine bestehende hosts.conf als Klartext, solange
|
|
* fuer diesen Agenten serverseitig noch nichts konfiguriert ist. Ist bereits etwas
|
|
* hinterlegt, passiert nichts (imported = 0) - die im Frontend gepflegte Liste hat
|
|
* Vorrang vor der lokalen Datei.
|
|
*
|
|
* @param array<string, string> $params
|
|
* @param array<string, mixed> $agent
|
|
*/
|
|
public function import(Request $request, array $params, array $agent): Response
|
|
{
|
|
if (($agent['role'] ?? '') !== 'ops') {
|
|
return Response::error('Nur Agenten der Rolle "ops" haben eine hosts.conf.', 403);
|
|
}
|
|
|
|
$rows = HostConfigRepository::parseHostsConf($request->rawBody);
|
|
$imported = $this->hosts->importForAgent((int) $agent['id'], $rows);
|
|
|
|
return Response::json(['imported' => $imported]);
|
|
}
|
|
}
|