KEA Setup
This commit is contained in:
@@ -17,13 +17,20 @@
|
|||||||
},
|
},
|
||||||
"setup": {
|
"setup": {
|
||||||
"fields": [
|
"fields": [
|
||||||
{ "name": "db.driver", "label": "DB Driver", "type": "text", "required": true },
|
{ "name": "db.driver", "label": "KEA DB Driver", "type": "text", "required": true, "help": "Standard-KEA-Datenbank, die auch vom KEA-Dienst selbst genutzt wird." },
|
||||||
{ "name": "db.host", "label": "DB Host", "type": "text", "required": true },
|
{ "name": "db.host", "label": "KEA DB Host", "type": "text", "required": true },
|
||||||
{ "name": "db.port", "label": "DB Port", "type": "number", "required": true },
|
{ "name": "db.port", "label": "KEA DB Port", "type": "number", "required": true },
|
||||||
{ "name": "db.dbname", "label": "DB Name", "type": "text", "required": true },
|
{ "name": "db.dbname", "label": "KEA DB Name", "type": "text", "required": true },
|
||||||
{ "name": "db.schema", "label": "DB Schema", "type": "text", "required": false },
|
{ "name": "db.schema", "label": "KEA DB Schema", "type": "text", "required": false },
|
||||||
{ "name": "db.user", "label": "DB User", "type": "text", "required": true },
|
{ "name": "db.user", "label": "KEA DB User", "type": "text", "required": true },
|
||||||
{ "name": "db.password", "label": "DB Passwort", "type": "password", "required": true },
|
{ "name": "db.password", "label": "KEA DB Passwort", "type": "password", "required": true },
|
||||||
|
{ "name": "metadata_db.driver", "label": "Nexus DHCP DB Driver", "type": "text", "required": true, "help": "Separate Datenbank fuer Nexus-eigene DHCP-Zusatzinfos, nicht fuer KEA-Standardtabellen." },
|
||||||
|
{ "name": "metadata_db.host", "label": "Nexus DHCP DB Host", "type": "text", "required": true },
|
||||||
|
{ "name": "metadata_db.port", "label": "Nexus DHCP DB Port", "type": "number", "required": true },
|
||||||
|
{ "name": "metadata_db.dbname", "label": "Nexus DHCP DB Name", "type": "text", "required": true },
|
||||||
|
{ "name": "metadata_db.schema", "label": "Nexus DHCP DB Schema", "type": "text", "required": false },
|
||||||
|
{ "name": "metadata_db.user", "label": "Nexus DHCP DB User", "type": "text", "required": true },
|
||||||
|
{ "name": "metadata_db.password", "label": "Nexus DHCP DB Passwort", "type": "password", "required": true },
|
||||||
{ "name": "kea_db_version", "label": "KEA DB Version", "type": "text", "required": false },
|
{ "name": "kea_db_version", "label": "KEA DB Version", "type": "text", "required": false },
|
||||||
{ "name": "kea_init_script", "label": "KEA Init Script", "type": "text", "required": false },
|
{ "name": "kea_init_script", "label": "KEA Init Script", "type": "text", "required": false },
|
||||||
{ "name": "kea_init_cmd", "label": "KEA Init Command", "type": "text", "required": false },
|
{ "name": "kea_init_cmd", "label": "KEA Init Command", "type": "text", "required": false },
|
||||||
@@ -38,5 +45,14 @@
|
|||||||
"schema": "public",
|
"schema": "public",
|
||||||
"user": "",
|
"user": "",
|
||||||
"password": ""
|
"password": ""
|
||||||
|
},
|
||||||
|
"metadata_db_defaults": {
|
||||||
|
"driver": "pgsql",
|
||||||
|
"host": "192.168.178.10",
|
||||||
|
"port": 5432,
|
||||||
|
"dbname": "",
|
||||||
|
"schema": "public",
|
||||||
|
"user": "",
|
||||||
|
"password": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,28 @@
|
|||||||
<?php
|
<?php
|
||||||
|
use App\Database;
|
||||||
use App\Repository\KeaHostRepository;
|
use App\Repository\KeaHostRepository;
|
||||||
|
use App\Repository\KeaHostMetadataRepository;
|
||||||
|
|
||||||
$module = modules()->get('kea');
|
$module = modules()->get('kea');
|
||||||
$fallback = $module['db_defaults'] ?? [];
|
$fallback = $module['db_defaults'] ?? [];
|
||||||
|
|
||||||
$pdo = modules()->modulePdo('kea', $fallback);
|
$pdo = modules()->modulePdo('kea', $fallback);
|
||||||
|
$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;
|
||||||
|
$metadataRepo = null;
|
||||||
$hosts = [];
|
$hosts = [];
|
||||||
$error = null;
|
$error = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$repo = new KeaHostRepository($pdo);
|
if (!empty($metadataConfig['driver']) && !empty($metadataConfig['dbname'])) {
|
||||||
|
$metadataRepo = new KeaHostMetadataRepository(Database::createFromArray($metadataConfig));
|
||||||
|
$metadataRepo->ensureSchema();
|
||||||
|
}
|
||||||
|
|
||||||
|
$repo = new KeaHostRepository($pdo, $metadataRepo);
|
||||||
$hosts = $repo->findAll(50);
|
$hosts = $repo->findAll(50);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$error = "Datenbankfehler: " . $e->getMessage();
|
$error = "Datenbankfehler: " . $e->getMessage();
|
||||||
|
|||||||
@@ -35,7 +35,8 @@
|
|||||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Hostname</th>
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Hostname</th>
|
||||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">IP Adresse</th>
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">IP Adresse</th>
|
||||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">MAC Adresse</th>
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">MAC Adresse</th>
|
||||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Kontext</th>
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Echter Name</th>
|
||||||
|
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Standort</th>
|
||||||
<th scope="col" class="relative px-6 py-3">
|
<th scope="col" class="relative px-6 py-3">
|
||||||
<span class="sr-only">Edit</span>
|
<span class="sr-only">Edit</span>
|
||||||
</th>
|
</th>
|
||||||
@@ -44,7 +45,7 @@
|
|||||||
<tbody class="bg-gray-800 divide-y divide-gray-700">
|
<tbody class="bg-gray-800 divide-y divide-gray-700">
|
||||||
<?php if (empty($hosts)): ?>
|
<?php if (empty($hosts)): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5" class="px-6 py-4 text-center text-sm text-gray-500">Keine Hosts gefunden.</td>
|
<td colspan="6" class="px-6 py-4 text-center text-sm text-gray-500">Keine Hosts gefunden.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php foreach ($hosts as $host): ?>
|
<?php foreach ($hosts as $host): ?>
|
||||||
@@ -59,7 +60,10 @@
|
|||||||
<?= e($host['dhcp_identifier']) ?>
|
<?= e($host['dhcp_identifier']) ?>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-400">
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-400">
|
||||||
<?= e($host['user_context'] ?? '-') ?>
|
<?= e((string)($host['metadata']['real_name'] ?? '-')) ?>
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-400">
|
||||||
|
<?= e((string)($host['metadata']['location'] ?? '-')) ?>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||||
<a href="#" class="text-indigo-400 hover:text-indigo-300">Bearbeiten</a>
|
<a href="#" class="text-indigo-400 hover:text-indigo-300">Bearbeiten</a>
|
||||||
|
|||||||
@@ -28,10 +28,39 @@ $defaults = $module['db_defaults'] ?? [];
|
|||||||
if (empty($current['db']) && is_array($defaults)) {
|
if (empty($current['db']) && is_array($defaults)) {
|
||||||
$current['db'] = $defaults;
|
$current['db'] = $defaults;
|
||||||
}
|
}
|
||||||
|
$metadataDefaults = $module['metadata_db_defaults'] ?? [];
|
||||||
|
if (empty($current['metadata_db']) && is_array($metadataDefaults)) {
|
||||||
|
$current['metadata_db'] = $metadataDefaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
$setNested = function (array &$target, string $path, mixed $value): void {
|
||||||
|
$parts = explode('.', $path);
|
||||||
|
$last = array_pop($parts);
|
||||||
|
$node = &$target;
|
||||||
|
foreach ($parts as $part) {
|
||||||
|
if (!isset($node[$part]) || !is_array($node[$part])) {
|
||||||
|
$node[$part] = [];
|
||||||
|
}
|
||||||
|
$node = &$node[$part];
|
||||||
|
}
|
||||||
|
if ($last !== null && $last !== '') {
|
||||||
|
$node[$last] = $value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$getNested = function (array $source, string $path): mixed {
|
||||||
|
$node = $source;
|
||||||
|
foreach (explode('.', $path) as $part) {
|
||||||
|
if (!is_array($node) || !array_key_exists($part, $node)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$node = $node[$part];
|
||||||
|
}
|
||||||
|
return $node;
|
||||||
|
};
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$payload = [];
|
$payload = [];
|
||||||
$db = $current['db'] ?? [];
|
|
||||||
|
|
||||||
foreach ($fields as $field) {
|
foreach ($fields as $field) {
|
||||||
$name = (string)($field['name'] ?? '');
|
$name = (string)($field['name'] ?? '');
|
||||||
@@ -55,19 +84,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str_starts_with($name, 'db.')) {
|
if (str_contains($name, '.')) {
|
||||||
$key = substr($name, 3);
|
$setNested($payload, $name, $value);
|
||||||
$db[$key] = $value;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$payload[$name] = $value;
|
$payload[$name] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($db)) {
|
|
||||||
$payload['db'] = $db;
|
|
||||||
}
|
|
||||||
|
|
||||||
modules()->saveSettings($moduleName, $payload);
|
modules()->saveSettings($moduleName, $payload);
|
||||||
modules()->saveAuth($moduleName, [
|
modules()->saveAuth($moduleName, [
|
||||||
'required' => isset($_POST['auth_required']),
|
'required' => isset($_POST['auth_required']),
|
||||||
@@ -107,9 +131,8 @@ $authConfig = is_array($module['auth'] ?? null) ? $module['auth'] : ['required'
|
|||||||
$value = '';
|
$value = '';
|
||||||
if ($name === 'kea_auto_init') {
|
if ($name === 'kea_auto_init') {
|
||||||
$value = !empty($current[$name]) ? '1' : '0';
|
$value = !empty($current[$name]) ? '1' : '0';
|
||||||
} elseif (str_starts_with($name, 'db.')) {
|
} elseif (str_contains($name, '.')) {
|
||||||
$key = substr($name, 3);
|
$value = (string)($getNested($current, $name) ?? '');
|
||||||
$value = (string)($current['db'][$key] ?? '');
|
|
||||||
} else {
|
} else {
|
||||||
$value = (string)($current[$name] ?? '');
|
$value = (string)($current[$name] ?? '');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,10 +98,6 @@ final class Database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// After init, ensure our metadata table exists (non-invasive)
|
|
||||||
if (self::tableExists($pdo, 'hosts')) {
|
|
||||||
self::ensureNexusTables($pdo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function tableExists(\PDO $pdo, string $table): bool
|
private static function tableExists(\PDO $pdo, string $table): bool
|
||||||
@@ -179,37 +175,6 @@ final class Database
|
|||||||
$pdo->exec($sql);
|
$pdo->exec($sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function ensureNexusTables(\PDO $pdo): void
|
|
||||||
{
|
|
||||||
$pdo->exec(
|
|
||||||
"CREATE TABLE IF NOT EXISTS nexus_host_meta (
|
|
||||||
host_id BIGINT PRIMARY KEY,
|
|
||||||
location TEXT,
|
|
||||||
device_type TEXT,
|
|
||||||
owner TEXT,
|
|
||||||
tags JSONB,
|
|
||||||
notes TEXT,
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
||||||
)"
|
|
||||||
);
|
|
||||||
|
|
||||||
$pdo->exec(
|
|
||||||
"DO $$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (
|
|
||||||
SELECT 1 FROM information_schema.table_constraints
|
|
||||||
WHERE constraint_name = 'fk_nexus_host_meta_host'
|
|
||||||
) THEN
|
|
||||||
ALTER TABLE nexus_host_meta
|
|
||||||
ADD CONSTRAINT fk_nexus_host_meta_host
|
|
||||||
FOREIGN KEY (host_id)
|
|
||||||
REFERENCES hosts(host_id)
|
|
||||||
ON DELETE CASCADE;
|
|
||||||
END IF;
|
|
||||||
END $$;"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function buildMysqlDsn(array $db): string
|
private static function buildMysqlDsn(array $db): string
|
||||||
{
|
{
|
||||||
if (empty($db['dbname'])) {
|
if (empty($db['dbname'])) {
|
||||||
|
|||||||
@@ -258,6 +258,7 @@ final class ModuleManager
|
|||||||
'menu' => $data['menu'] ?? [],
|
'menu' => $data['menu'] ?? [],
|
||||||
'sidebar' => $data['sidebar'] ?? [],
|
'sidebar' => $data['sidebar'] ?? [],
|
||||||
'db_defaults' => $data['db_defaults'] ?? [],
|
'db_defaults' => $data['db_defaults'] ?? [],
|
||||||
|
'metadata_db_defaults' => $data['metadata_db_defaults'] ?? [],
|
||||||
'path' => $dir,
|
'path' => $dir,
|
||||||
'entry' => '/module/' . rawurlencode($name),
|
'entry' => '/module/' . rawurlencode($name),
|
||||||
'auth' => is_array($data['auth'] ?? null) ? $data['auth'] : ['required' => false, 'users' => [], 'groups' => []],
|
'auth' => is_array($data['auth'] ?? null) ? $data['auth'] : ['required' => false, 'users' => [], 'groups' => []],
|
||||||
|
|||||||
170
src/Repository/KeaHostMetadataRepository.php
Normal file
170
src/Repository/KeaHostMetadataRepository.php
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
final class KeaHostMetadataRepository
|
||||||
|
{
|
||||||
|
public function __construct(private PDO $pdo) {}
|
||||||
|
|
||||||
|
public function ensureSchema(): void
|
||||||
|
{
|
||||||
|
$driver = (string)$this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||||
|
|
||||||
|
if ($driver === 'pgsql') {
|
||||||
|
$this->pdo->exec(
|
||||||
|
"CREATE TABLE IF NOT EXISTS nexus_dhcp_host_meta (
|
||||||
|
host_id BIGINT PRIMARY KEY,
|
||||||
|
hardware_address TEXT,
|
||||||
|
ip_address TEXT,
|
||||||
|
real_name TEXT,
|
||||||
|
device_name TEXT,
|
||||||
|
owner TEXT,
|
||||||
|
location TEXT,
|
||||||
|
device_type TEXT,
|
||||||
|
notes TEXT,
|
||||||
|
tags_json JSONB,
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
)"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($driver === 'sqlite') {
|
||||||
|
$this->pdo->exec(
|
||||||
|
"CREATE TABLE IF NOT EXISTS nexus_dhcp_host_meta (
|
||||||
|
host_id INTEGER PRIMARY KEY,
|
||||||
|
hardware_address TEXT,
|
||||||
|
ip_address TEXT,
|
||||||
|
real_name TEXT,
|
||||||
|
device_name TEXT,
|
||||||
|
owner TEXT,
|
||||||
|
location TEXT,
|
||||||
|
device_type TEXT,
|
||||||
|
notes TEXT,
|
||||||
|
tags_json TEXT,
|
||||||
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
|
)"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pdo->exec(
|
||||||
|
"CREATE TABLE IF NOT EXISTS nexus_dhcp_host_meta (
|
||||||
|
host_id BIGINT PRIMARY KEY,
|
||||||
|
hardware_address TEXT,
|
||||||
|
ip_address TEXT,
|
||||||
|
real_name TEXT,
|
||||||
|
device_name TEXT,
|
||||||
|
owner TEXT,
|
||||||
|
location TEXT,
|
||||||
|
device_type TEXT,
|
||||||
|
notes TEXT,
|
||||||
|
tags_json TEXT,
|
||||||
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findByHostIds(array $hostIds): array
|
||||||
|
{
|
||||||
|
$hostIds = array_values(array_unique(array_filter(array_map('intval', $hostIds))));
|
||||||
|
if ($hostIds === []) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$params = [];
|
||||||
|
$placeholders = [];
|
||||||
|
foreach ($hostIds as $idx => $hostId) {
|
||||||
|
$key = ':host_id_' . $idx;
|
||||||
|
$placeholders[] = $key;
|
||||||
|
$params[$key] = $hostId;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare(
|
||||||
|
'SELECT host_id, hardware_address, ip_address, real_name, device_name, owner, location, device_type, notes, tags_json, updated_at
|
||||||
|
FROM nexus_dhcp_host_meta
|
||||||
|
WHERE host_id IN (' . implode(', ', $placeholders) . ')'
|
||||||
|
);
|
||||||
|
foreach ($params as $key => $value) {
|
||||||
|
$stmt->bindValue($key, $value, PDO::PARAM_INT);
|
||||||
|
}
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||||
|
$items[(int)$row['host_id']] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveForHost(int $hostId, string $hardwareAddress, string $ipAddress, array $metadata): void
|
||||||
|
{
|
||||||
|
$metadata = array_merge([
|
||||||
|
'real_name' => null,
|
||||||
|
'device_name' => null,
|
||||||
|
'owner' => null,
|
||||||
|
'location' => null,
|
||||||
|
'device_type' => null,
|
||||||
|
'notes' => null,
|
||||||
|
'tags' => [],
|
||||||
|
], $metadata);
|
||||||
|
|
||||||
|
$tagsJson = json_encode($metadata['tags'], JSON_UNESCAPED_UNICODE);
|
||||||
|
if ($tagsJson === false) {
|
||||||
|
$tagsJson = '[]';
|
||||||
|
}
|
||||||
|
|
||||||
|
$driver = (string)$this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||||
|
if ($driver === 'pgsql') {
|
||||||
|
$stmt = $this->pdo->prepare(
|
||||||
|
"INSERT INTO nexus_dhcp_host_meta (
|
||||||
|
host_id, hardware_address, ip_address, real_name, device_name, owner, location, device_type, notes, tags_json, updated_at
|
||||||
|
) VALUES (
|
||||||
|
:host_id, :hardware_address, :ip_address, :real_name, :device_name, :owner, :location, :device_type, :notes, CAST(:tags_json AS jsonb), NOW()
|
||||||
|
)
|
||||||
|
ON CONFLICT (host_id) DO UPDATE SET
|
||||||
|
hardware_address = EXCLUDED.hardware_address,
|
||||||
|
ip_address = EXCLUDED.ip_address,
|
||||||
|
real_name = EXCLUDED.real_name,
|
||||||
|
device_name = EXCLUDED.device_name,
|
||||||
|
owner = EXCLUDED.owner,
|
||||||
|
location = EXCLUDED.location,
|
||||||
|
device_type = EXCLUDED.device_type,
|
||||||
|
notes = EXCLUDED.notes,
|
||||||
|
tags_json = EXCLUDED.tags_json,
|
||||||
|
updated_at = NOW()"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$stmt = $this->pdo->prepare(
|
||||||
|
"REPLACE INTO nexus_dhcp_host_meta (
|
||||||
|
host_id, hardware_address, ip_address, real_name, device_name, owner, location, device_type, notes, tags_json, updated_at
|
||||||
|
) VALUES (
|
||||||
|
:host_id, :hardware_address, :ip_address, :real_name, :device_name, :owner, :location, :device_type, :notes, :tags_json, CURRENT_TIMESTAMP
|
||||||
|
)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->execute([
|
||||||
|
'host_id' => $hostId,
|
||||||
|
'hardware_address' => $hardwareAddress,
|
||||||
|
'ip_address' => $ipAddress,
|
||||||
|
'real_name' => $this->nullableString($metadata['real_name']),
|
||||||
|
'device_name' => $this->nullableString($metadata['device_name']),
|
||||||
|
'owner' => $this->nullableString($metadata['owner']),
|
||||||
|
'location' => $this->nullableString($metadata['location']),
|
||||||
|
'device_type' => $this->nullableString($metadata['device_type']),
|
||||||
|
'notes' => $this->nullableString($metadata['notes']),
|
||||||
|
'tags_json' => $tagsJson,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function nullableString(mixed $value): ?string
|
||||||
|
{
|
||||||
|
$value = trim((string)$value);
|
||||||
|
return $value !== '' ? $value : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,11 +7,14 @@ use PDO;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository für das KEA DHCP Host-Management.
|
* Repository für das KEA DHCP Host-Management.
|
||||||
* Interagiert direkt mit der 'hosts' Tabelle in der PostgreSQL-Datenbank.
|
* Interagiert direkt mit der 'hosts' Tabelle in der KEA-Datenbank.
|
||||||
*/
|
*/
|
||||||
final class KeaHostRepository
|
final class KeaHostRepository
|
||||||
{
|
{
|
||||||
public function __construct(private PDO $pdo) {}
|
public function __construct(
|
||||||
|
private PDO $pdo,
|
||||||
|
private ?KeaHostMetadataRepository $metadata = null
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ruft eine Liste aller Host-Reservierungen ab (Geräte-Inventar).
|
* Ruft eine Liste aller Host-Reservierungen ab (Geräte-Inventar).
|
||||||
@@ -29,7 +32,7 @@ final class KeaHostRepository
|
|||||||
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
|
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
|
|
||||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
return $this->withMetadata($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
} catch (\PDOException $e) {
|
} catch (\PDOException $e) {
|
||||||
if ($this->isMissingTable($e)) {
|
if ($this->isMissingTable($e)) {
|
||||||
throw new \RuntimeException(
|
throw new \RuntimeException(
|
||||||
@@ -85,17 +88,14 @@ final class KeaHostRepository
|
|||||||
* @param string $ip IPv4-Adresse
|
* @param string $ip IPv4-Adresse
|
||||||
* @param int $subnetId Die ID des Subnets (dhcp4_subnet_id), in dem die IP liegt
|
* @param int $subnetId Die ID des Subnets (dhcp4_subnet_id), in dem die IP liegt
|
||||||
* @param string|null $hostname Optionaler Hostname
|
* @param string|null $hostname Optionaler Hostname
|
||||||
* @param array $metadata Zusätzliche Infos (Standort, Verantwortlicher etc.) für 'user_context'
|
* @param array $metadata Zusätzliche Infos fuer die separate Nexus-DHCP-Metadatenbank.
|
||||||
*/
|
*/
|
||||||
public function create(string $mac, string $ip, int $subnetId, ?string $hostname = null, array $metadata = []): int
|
public function create(string $mac, string $ip, int $subnetId, ?string $hostname = null, array $metadata = []): int
|
||||||
{
|
{
|
||||||
// Metadaten werden im KEA-Standardfeld 'user_context' als JSON gespeichert
|
|
||||||
$userContextJson = json_encode($metadata, JSON_THROW_ON_ERROR);
|
|
||||||
|
|
||||||
// dhcp_identifier_type 1 = HW_ADDRESS (Ethernet)
|
// dhcp_identifier_type 1 = HW_ADDRESS (Ethernet)
|
||||||
$stmt = $this->pdo->prepare(
|
$stmt = $this->pdo->prepare(
|
||||||
'INSERT INTO hosts (dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id, ipv4_address, hostname, user_context)
|
'INSERT INTO hosts (dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id, ipv4_address, hostname)
|
||||||
VALUES (:mac, 1, :subnetId, :ip, :hostname, :user_context)'
|
VALUES (:mac, 1, :subnetId, :ip, :hostname)'
|
||||||
);
|
);
|
||||||
|
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
@@ -103,10 +103,30 @@ final class KeaHostRepository
|
|||||||
'subnetId' => $subnetId,
|
'subnetId' => $subnetId,
|
||||||
'ip' => $ip,
|
'ip' => $ip,
|
||||||
'hostname' => $hostname,
|
'hostname' => $hostname,
|
||||||
'user_context' => $userContextJson,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $this->lastInsertIdSafe();
|
$hostId = $this->lastInsertIdSafe();
|
||||||
|
if ($this->metadata !== null && $metadata !== []) {
|
||||||
|
$this->metadata->saveForHost($hostId, $mac, $ip, $metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $hostId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function withMetadata(array $hosts): array
|
||||||
|
{
|
||||||
|
if ($hosts === [] || $this->metadata === null) {
|
||||||
|
return $hosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
$metadataByHost = $this->metadata->findByHostIds(array_column($hosts, 'host_id'));
|
||||||
|
foreach ($hosts as &$host) {
|
||||||
|
$hostId = (int)($host['host_id'] ?? 0);
|
||||||
|
$host['metadata'] = $metadataByHost[$hostId] ?? [];
|
||||||
|
}
|
||||||
|
unset($host);
|
||||||
|
|
||||||
|
return $hosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function driver(): string
|
private function driver(): string
|
||||||
|
|||||||
Reference in New Issue
Block a user