OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

host_form.php

16 KB
<?php
$this->layout('layouts/main', [
    'page_title'    => $page_title,
    'current_route' => 'vms',
    'full_width'    => true,
]);

$host       = $host       ?? null;
$clusters   = $clusters   ?? [];
$can_manage = $can_manage ?? false;
$isEdit     = $host !== null;

$hypervisors = [
    'proxmox'     => 'Proxmox VE',
    'vmware_esxi' => 'VMware vSphere / ESXi',
    'hyper_v'     => 'Microsoft Hyper-V',
    'kvm'         => 'KVM / QEMU',
    'xen'         => 'Xen',
    'other'       => 'Sonstige',
];
?>

<div class="vhf-wrap">

    <div class="content-header">
        <div>
            <h1><?= $this->e($page_title) ?></h1>
            <ol class="breadcrumb">
                <li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
                <li class="breadcrumb-item"><a href="/vms"><?= $this->te('Virtualisierung') ?></a></li>
                <li class="breadcrumb-item"><a href="/vms/hosts"><?= $this->te('Hosts') ?></a></li>
                <?php if ($isEdit): ?>
                    <li class="breadcrumb-item"><a href="/vms/hosts/<?= $host['id'] ?>"><?= $this->e($host['name']) ?></a></li>
                    <li class="breadcrumb-item active"><?= $this->te('Bearbeiten') ?></li>
                <?php else: ?>
                    <li class="breadcrumb-item active"><?= $this->te('Neuer Host') ?></li>
                <?php endif; ?>
            </ol>
        </div>
    </div>

    <form action="<?= $this->e($form_action) ?>" method="POST" class="vhf-layout">
        <input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">

        <!-- Hauptspalte -->
        <div class="vhf-main">

            <section class="vhf-card">
                <h2 class="vhf-section-head">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/></svg>
                    <?= $this->te('Stammdaten') ?>
                </h2>
                <div class="vhf-card-body">
                    <div class="vhf-row-2">
                        <div class="vhf-field">
                            <label for="vhf_name"><?= $this->te('Hostname') ?> <span class="vhf-req">*</span></label>
                            <input type="text" id="vhf_name" name="name" required maxlength="255"
                                   value="<?= $this->e($host['name'] ?? '') ?>"
                                   placeholder="<?= $this->te('z.B. pve-node-01') ?>">
                        </div>
                        <div class="vhf-field">
                            <label for="vhf_status"><?= $this->te('Status') ?></label>
                            <select id="vhf_status" name="status">
                                <option value="online"      <?= ($host['status'] ?? 'online') === 'online'      ? 'selected' : '' ?>><?= $this->te('Online') ?></option>
                                <option value="offline"     <?= ($host['status'] ?? '') === 'offline'     ? 'selected' : '' ?>><?= $this->te('Offline') ?></option>
                                <option value="maintenance" <?= ($host['status'] ?? '') === 'maintenance' ? 'selected' : '' ?>><?= $this->te('Wartung') ?></option>
                            </select>
                        </div>
                    </div>
                    <div class="vhf-row-2">
                        <div class="vhf-field">
                            <label for="vhf_ip"><?= $this->te('IP-Adresse') ?></label>
                            <input type="text" id="vhf_ip" name="ip_address" maxlength="45"
                                   value="<?= $this->e($host['ip_address'] ?? '') ?>"
                                   placeholder="z.B. 192.168.1.10">
                            <span class="vhf-hint"><?= $this->te('Wird automatisch in IPAM eingetragen, wenn ein Subnetz passt.') ?></span>
                        </div>
                        <div class="vhf-field">
                            <label for="vhf_location"><?= $this->te('Standort / Rack') ?></label>
                            <input type="text" id="vhf_location" name="location" maxlength="255"
                                   value="<?= $this->e($host['location'] ?? '') ?>"
                                   placeholder="<?= $this->te('z.B. RZ-Berlin · Rack 3') ?>">
                        </div>
                    </div>
                    <div class="vhf-field">
                        <label for="vhf_hypervisor"><?= $this->te('Hypervisor-Typ') ?></label>
                        <select id="vhf_hypervisor" name="hypervisor">
                            <?php foreach ($hypervisors as $val => $label): ?>
                                <option value="<?= $val ?>" <?= ($host['hypervisor'] ?? 'proxmox') === $val ? 'selected' : '' ?>>
                                    <?= $this->e($label) ?>
                                </option>
                            <?php endforeach; ?>
                        </select>
                    </div>
                    <div class="vhf-field">
                        <label for="vhf_cluster"><?= $this->te('Cluster') ?></label>
                        <select id="vhf_cluster" name="cluster_id">
                            <option value=""><?= $this->te('Kein Cluster') ?></option>
                            <?php foreach ($clusters as $c): ?>
                                <option value="<?= $c['id'] ?>" <?= ($host['cluster_id'] ?? '') == $c['id'] ? 'selected' : '' ?>>
                                    <?= $this->e($c['name']) ?>
                                </option>
                            <?php endforeach; ?>
                        </select>
                    </div>
                    <div class="vhf-field">
                        <label for="vhf_desc"><?= $this->te('Beschreibung') ?></label>
                        <textarea id="vhf_desc" name="description" rows="3"
                                  placeholder="<?= $this->te('Weitere Informationen zum Host…') ?>"><?= $this->e($host['description'] ?? '') ?></textarea>
                    </div>
                </div>
            </section>

            <section class="vhf-card">
                <h2 class="vhf-section-head">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2z"/><path d="M12 6v6l4 2"/></svg>
                    <?= $this->te('Ressourcen') ?>
                </h2>
                <div class="vhf-card-body">
                    <div class="vhf-row-3">
                        <div class="vhf-field">
                            <label for="vhf_cpu"><?= $this->te('CPU-Kerne') ?></label>
                            <input type="number" id="vhf_cpu" name="cpu_cores" min="1" max="10000"
                                   value="<?= $this->e($host['cpu_cores'] ?? '') ?>"
                                   placeholder="z.B. 32">
                        </div>
                        <div class="vhf-field">
                            <label for="vhf_ram"><?= $this->t('RAM (GB)') ?></label>
                            <input type="number" id="vhf_ram" name="ram_gb" min="0" step="0.5"
                                   value="<?= $this->e($host['ram_gb'] ?? '') ?>"
                                   placeholder="z.B. 256">
                        </div>
                        <div class="vhf-field">
                            <label for="vhf_disk"><?= $this->te('Speicher (GB)') ?></label>
                            <input type="number" id="vhf_disk" name="storage_gb" min="0" step="1"
                                   value="<?= $this->e($host['storage_gb'] ?? '') ?>"
                                   placeholder="z.B. 4000">
                        </div>
                    </div>
                </div>
            </section>

        </div><!-- /vhf-main -->

        <!-- Sidebar -->
        <aside class="vhf-sidebar">

            <?php if ($isEdit && !empty($host['hypervisor_url'])): ?>
            <section class="vhf-card">
                <h2 class="vhf-section-head">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M5 12.55a11 11 0 0 1 14.08 0"/><path d="M1.42 9a16 16 0 0 1 21.16 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><line x1="12" y1="20" x2="12.01" y2="20"/></svg>
                    <?= $this->te('API-Synchronisierung') ?>
                </h2>
                <div class="vhf-card-body">
                    <p class="vhf-hint" style="font-size:.82rem;color:var(--gray-700);">
                        <?= $this->t('API-Verbindung konfiguriert.<br>') ?>
                        <?php
                        $syncStatus = $host['sync_status'] ?? 'never';
                        $syncColors = ['ok' => '#15803d', 'error' => '#b91c1c', 'never' => '#9ca3af'];
                        $syncLabels = ['ok' => 'Letzter Sync erfolgreich', 'error' => 'Letzter Sync fehlgeschlagen', 'never' => 'Noch nicht synchronisiert'];
                        ?>
                        <span style="color:<?= $syncColors[$syncStatus] ?? 'var(--gray-400)' ?>;font-weight:600;">
                            <?= $syncLabels[$syncStatus] ?? 'Unbekannt' ?>
                        </span>
                        <?php if ($host['last_sync_at'] ?? null): ?>
                            <br><span style="color:var(--gray-400);font-size:.75rem;"><?= substr($host['last_sync_at'], 0, 16) ?></span>
                        <?php endif; ?>
                    </p>
                    <a href="/admin/vms/hosts/<?= $host['id'] ?>/sync-settings" class="btn btn-outline-primary" style="margin-top:.25rem;">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="13" height="13"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M4.93 4.93a10 10 0 0 0 0 14.14"/></svg>
                        <?= $this->te('API-Einstellungen konfigurieren') ?>
                    </a>
                </div>
            </section>
            <?php elseif ($isEdit): ?>
            <section class="vhf-card">
                <h2 class="vhf-section-head">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><path d="M5 12.55a11 11 0 0 1 14.08 0"/><line x1="12" y1="20" x2="12.01" y2="20"/></svg>
                    <?= $this->te('API-Synchronisierung') ?>
                </h2>
                <div class="vhf-card-body">
                    <p class="vhf-hint" style="font-size:.82rem;">
                        <?= $this->te('Noch keine API-Verbindung konfiguriert.') ?>
                    </p>
                    <a href="/admin/vms/hosts/<?= $host['id'] ?>/sync-settings" class="btn btn-outline-primary">
                        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="13" height="13"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M4.93 4.93a10 10 0 0 0 0 14.14"/></svg>
                        <?= $this->te('API-Verbindung einrichten') ?>
                    </a>
                </div>
            </section>
            <?php endif; ?>

            <?php if (!empty($clusters)): ?>
            <section class="vhf-card">
                <h2 class="vhf-section-head">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><circle cx="12" cy="5" r="3"/><circle cx="5" cy="19" r="3"/><circle cx="19" cy="19" r="3"/><line x1="12" y1="8" x2="5" y2="16"/><line x1="12" y1="8" x2="19" y2="16"/></svg>
                    <?= $this->te('Cluster') ?>
                </h2>
                <div class="vhf-card-body">
                    <ul class="vhf-cluster-list">
                        <?php foreach ($clusters as $c): ?>
                        <li class="vhf-cluster-item <?= ($host['cluster_id'] ?? '') == $c['id'] ? 'vhf-cluster-selected' : '' ?>">
                            <span class="vhf-cluster-name"><?= $this->e($c['name']) ?></span>
                            <span class="vhf-cluster-info"><?= $this->te('{anzahl} Hosts', ['anzahl' => (int)($c['host_count'] ?? 0)]) ?></span>
                        </li>
                        <?php endforeach; ?>
                    </ul>
                </div>
            </section>
            <?php endif; ?>

            <div class="vhf-actions">
                <button type="submit" class="btn btn-primary">
                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><polyline points="20 6 9 17 4 12"/></svg>
                    <?= $isEdit ? 'Änderungen speichern' : 'Host erstellen' ?>
                </button>
                <a href="<?= $isEdit ? '/vms/hosts/' . $host['id'] : '/vms/hosts' ?>" class="btn btn-default"><?= $this->te('Abbrechen') ?></a>
            </div>

        </aside>
    </form>

</div>

<style>
/* ====================================================
   Host-Formular  ·  Prefix: vhf-
   ==================================================== */
.vhf-wrap { display:flex; flex-direction:column; }

.vhf-layout { display:grid; grid-template-columns:1fr 310px; gap:1.5rem; padding:1.5rem 1rem 3rem; background:var(--gray-50); }

.vhf-main { display:flex; flex-direction:column; gap:1.25rem; }
.vhf-sidebar { display:flex; flex-direction:column; gap:1rem; }

.vhf-card { background:var(--color-bg); border-radius:0.25rem; box-shadow:0 1px 3px rgba(0,0,0,0.06); overflow:hidden; }
.vhf-section-head {
    display:flex; align-items:center; gap:0.5rem;
    font-size:0.8rem; font-weight:700; color:var(--gray-700);
    text-transform:uppercase; letter-spacing:0.04em;
    padding:0.875rem 1.25rem; border-bottom:1px solid var(--gray-100); margin:0;
    background:var(--gray-50);
}
.vhf-section-head svg { color:var(--gray-400); flex-shrink:0; }
.vhf-card-body { padding:1.25rem; display:flex; flex-direction:column; gap:0.875rem; }

.vhf-field { display:flex; flex-direction:column; gap:0.35rem; }
.vhf-field label { font-size:0.8rem; font-weight:600; color:var(--gray-700); }
.vhf-field input, .vhf-field select, .vhf-field textarea {
    width:100%; padding:0.5rem 0.75rem; border:1px solid var(--gray-300);
    border-radius:0.25rem; font-size:0.875rem; color:var(--gray-900);
    background:var(--color-bg); transition:border-color .15s; font-family:inherit;
}
.vhf-field input:focus, .vhf-field select:focus, .vhf-field textarea:focus {
    outline:none; border-color:var(--color-primary); box-shadow:0 0 0 2px rgba(var(--color-primary-rgb), 0.15);
}
.vhf-field textarea { resize:vertical; }
.vhf-hint { font-size:0.75rem; color:var(--gray-400); }
.vhf-req { color:var(--red-600); }
.vhf-row-2 { display:grid; grid-template-columns:1fr 1fr; gap:0.875rem; }
.vhf-row-3 { display:grid; grid-template-columns:1fr 1fr 1fr; gap:0.875rem; }

/* Cluster List */
.vhf-cluster-list { margin:0; padding:0; list-style:none; display:flex; flex-direction:column; gap:0.375rem; }
.vhf-cluster-item {
    display:flex; justify-content:space-between; align-items:center;
    padding:0.45rem 0.75rem; border-radius:0.25rem; border:1px solid var(--gray-100); background:var(--gray-50);
}
.vhf-cluster-selected { border-color:var(--color-primary); background:var(--blue-50); }
.vhf-cluster-name { font-size:0.82rem; font-weight:500; color:var(--gray-700); }
.vhf-cluster-info { font-size:0.72rem; color:var(--gray-400); }

/* Actions */
.vhf-actions { display:flex; flex-direction:column; gap:0.5rem; }

@media (max-width:1100px) { .vhf-layout { grid-template-columns:1fr 280px; } }
@media (max-width:900px)  { .vhf-layout { grid-template-columns:1fr; padding:1rem; } }
@media (max-width:640px)  { .vhf-row-2, .vhf-row-3 { grid-template-columns:1fr; } }
</style>