kea
All checks were successful
Deploy / deploy-staging (push) Successful in 6s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-04-15 02:48:23 +02:00
parent 7157c98dcb
commit 598348a4b6
8 changed files with 532 additions and 11 deletions

View File

@@ -19,6 +19,7 @@ $notice = null;
$host = null;
$metadataRepo = null;
$groups = [];
$availableIpsByGroup = [];
try {
$pdo = modules()->modulePdo('kea', $fallback);
@@ -64,11 +65,19 @@ try {
}
$host = $repo->findDisplayByKey($source, $id) ?: $host;
}
$usedIps = array_diff(
array_merge($repo->usedIpAddresses(), $metadataRepo->desiredIps()),
[(string)($host['ipv4_address'] ?? ''), (string)($host['metadata']['desired_ip'] ?? '')]
);
$availableIpsByGroup = $metadataRepo->availableIpsByGroup($usedIps);
} catch (Throwable $e) {
$error = $e->getMessage();
}
$metadata = is_array($host['metadata'] ?? null) ? $host['metadata'] : [];
$selectedGroup = (string)($metadata['group_name'] ?? '');
$selectedIp = (string)($metadata['desired_ip'] ?? '');
?>
<section class="kea-page">
<div class="section-head">
@@ -128,17 +137,19 @@ $metadata = is_array($host['metadata'] ?? null) ? $host['metadata'] : [];
</label>
<label class="setup-field">
<span>Gruppe</span>
<input type="text" name="group_name" list="kea-groups" value="<?= e((string)($metadata['group_name'] ?? '')) ?>">
<datalist id="kea-groups">
<select name="group_name" data-kea-group-select>
<option value="">Bitte waehlen</option>
<?php foreach ($groups as $group): ?>
<option value="<?= e($group) ?>"></option>
<option value="<?= e($group) ?>" <?= $selectedGroup === $group ? 'selected' : '' ?>><?= e($group) ?></option>
<?php endforeach; ?>
</datalist>
</select>
</label>
<label class="setup-field">
<span>Feste IP</span>
<input type="text" name="desired_ip" value="<?= e((string)($metadata['desired_ip'] ?? '')) ?>" placeholder="<?= e((string)($host['ipv4_address'] ?? '')) ?>">
<small class="muted">Wenn gesetzt, wird der Eintrag als KEA-Reservierung gespeichert.</small>
<select name="desired_ip" data-kea-ip-select data-selected-ip="<?= e($selectedIp) ?>">
<option value="">Erst Gruppe waehlen</option>
</select>
<small class="muted">Es werden nur freie IPs aus dem IP-Bereich der gewaehlten Gruppe angeboten.</small>
</label>
<label class="setup-field kea-edit-form__wide">
<span>Notizen</span>
@@ -151,5 +162,36 @@ $metadata = is_array($host['metadata'] ?? null) ? $host['metadata'] : [];
</div>
</form>
</div>
<script>
(() => {
const ipsByGroup = <?= json_encode($availableIpsByGroup, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>;
const groupSelect = document.querySelector('[data-kea-group-select]');
const ipSelect = document.querySelector('[data-kea-ip-select]');
if (!groupSelect || !ipSelect) {
return;
}
const selectedIp = ipSelect.dataset.selectedIp || '';
const renderIps = () => {
const ips = ipsByGroup[groupSelect.value] || [];
ipSelect.innerHTML = '';
const empty = document.createElement('option');
empty.value = '';
empty.textContent = groupSelect.value ? 'Keine feste IP' : 'Erst Gruppe waehlen';
ipSelect.appendChild(empty);
if (selectedIp && !ips.includes(selectedIp)) {
ips.unshift(selectedIp);
}
for (const ip of ips) {
const option = document.createElement('option');
option.value = ip;
option.textContent = ip;
option.selected = ip === selectedIp;
ipSelect.appendChild(option);
}
};
groupSelect.addEventListener('change', renderIps);
renderIps();
})();
</script>
<?php endif; ?>
</section>

View File

@@ -0,0 +1,167 @@
<?php
use App\Database;
use App\Repository\KeaHostMetadataRepository;
use App\Repository\KeaHostRepository;
$module = modules()->get('kea');
$settings = modules()->settings('kea');
$metadataFallback = is_array($module['metadata_db_defaults'] ?? null) ? $module['metadata_db_defaults'] : [];
$metadataConfig = is_array($settings['metadata_db'] ?? null)
? array_replace($metadataFallback, $settings['metadata_db'])
: $metadataFallback;
$fallback = $module['db_defaults'] ?? [];
$error = null;
$notice = null;
$groups = [];
$availableIpsByGroup = [];
try {
if (empty($metadataConfig['driver']) || empty($metadataConfig['dbname'])) {
throw new RuntimeException('Nexus DHCP Zusatzdatenbank ist nicht konfiguriert.');
}
$metadataRepo = new KeaHostMetadataRepository(Database::createFromArray($metadataConfig));
$metadataRepo->ensureSchema();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = (string)($_POST['action'] ?? '');
if ($action === 'save_group') {
$metadataRepo->saveGroup((string)($_POST['name'] ?? ''), (string)($_POST['description'] ?? ''));
$notice = 'Gruppe gespeichert.';
} elseif ($action === 'add_range') {
$metadataRepo->addRange(
(string)($_POST['group_name'] ?? ''),
(string)($_POST['start_ip'] ?? ''),
(string)($_POST['end_ip'] ?? '')
);
$notice = 'IP-Bereich gespeichert.';
}
}
$groups = $metadataRepo->listGroupsWithRanges();
$keaRepo = new KeaHostRepository(modules()->modulePdo('kea', $fallback), $metadataRepo);
$availableIpsByGroup = $metadataRepo->availableIpsByGroup(
array_merge($keaRepo->usedIpAddresses(), $metadataRepo->desiredIps()),
4096
);
} catch (Throwable $e) {
$error = $e->getMessage();
}
?>
<section class="kea-page">
<div class="section-head">
<div>
<h2 class="section-title">KEA Gruppen</h2>
<p>Gruppen und IP-Bereiche fuer DHCP-Reservierungen.</p>
</div>
<a class="nav-link" href="/module/kea">Zurueck</a>
</div>
<?php if ($error): ?>
<div class="kea-message kea-message--error" role="alert">
<strong>Fehler</strong>
<p><?= e($error) ?></p>
</div>
<?php elseif ($notice): ?>
<div class="kea-message kea-message--success"><?= e($notice) ?></div>
<?php endif; ?>
<div class="kea-panel">
<div class="kea-panel__head">
<div>
<span class="pill">Gruppe</span>
<h3>Gruppe anlegen</h3>
</div>
</div>
<form method="post" class="kea-edit-form">
<input type="hidden" name="action" value="save_group">
<label class="setup-field">
<span>Name</span>
<input type="text" name="name" required>
</label>
<label class="setup-field">
<span>Beschreibung</span>
<input type="text" name="description">
</label>
<div class="setup-actions kea-edit-form__wide">
<button class="cta-button" type="submit">Gruppe speichern</button>
</div>
</form>
</div>
<div class="kea-panel">
<div class="kea-panel__head">
<div>
<span class="pill">IP-Bereich</span>
<h3>Bereich zuweisen</h3>
</div>
</div>
<form method="post" class="kea-edit-form">
<input type="hidden" name="action" value="add_range">
<label class="setup-field">
<span>Gruppe</span>
<select name="group_name" required>
<option value="">Bitte waehlen</option>
<?php foreach ($groups as $group): ?>
<option value="<?= e((string)$group['name']) ?>"><?= e((string)$group['name']) ?></option>
<?php endforeach; ?>
</select>
</label>
<label class="setup-field">
<span>Start-IP</span>
<input type="text" name="start_ip" placeholder="192.168.178.50" required>
</label>
<label class="setup-field">
<span>End-IP</span>
<input type="text" name="end_ip" placeholder="192.168.178.99" required>
</label>
<div class="setup-actions kea-edit-form__wide">
<button class="cta-button" type="submit">Bereich speichern</button>
</div>
</form>
</div>
<div class="kea-panel">
<div class="kea-panel__head">
<div>
<span class="pill">Uebersicht</span>
<h3>Gruppen und freie IPs</h3>
</div>
</div>
<div class="kea-table-wrap">
<table class="kea-table">
<thead>
<tr>
<th>Gruppe</th>
<th>Beschreibung</th>
<th>Bereiche</th>
<th>Freie IPs</th>
</tr>
</thead>
<tbody>
<?php if ($groups === []): ?>
<tr><td colspan="4" class="kea-empty">Noch keine Gruppen definiert.</td></tr>
<?php else: ?>
<?php foreach ($groups as $group): ?>
<?php $available = $availableIpsByGroup[(string)$group['name']] ?? []; ?>
<tr>
<td><?= e((string)$group['name']) ?></td>
<td><?= e((string)($group['description'] ?? '-')) ?></td>
<td>
<?php if (($group['ranges'] ?? []) === []): ?>
<span class="muted">Kein Bereich</span>
<?php else: ?>
<?php foreach ($group['ranges'] as $range): ?>
<div class="mono"><?= e((string)$range['start_ip']) ?> - <?= e((string)$range['end_ip']) ?></div>
<?php endforeach; ?>
<?php endif; ?>
</td>
<td><?= e((string)count($available)) ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</section>

View File

@@ -20,6 +20,7 @@ $stats = [
'reservations' => 0,
'leases' => 0,
'groups' => [],
'free_ips' => [],
];
try {
@@ -48,6 +49,15 @@ try {
$stats['groups'][$group] = ($stats['groups'][$group] ?? 0) + 1;
}
}
if ($metadataRepo !== null) {
$stats['free_ips'] = array_map(
static fn(array $ips): int => count($ips),
$metadataRepo->availableIpsByGroup(
array_merge($repo->usedIpAddresses(), $metadataRepo->desiredIps()),
4096
)
);
}
} catch (\Exception $e) {
$error = "Datenbankfehler: " . $e->getMessage();
}