{% extends "tesm_core/base.html" %} {% from "tesm_core/macros.html" import field, checkbox, pill, empty_state, alert, search_box %} {% block page_title %}DHCP-Server{% endblock %} {% block page_sub %} Kea · {{ subnets | length }} Subnetze · {{ reservations | length }} Reservierungen {% endblock %} {% block content %} {% for warning in warnings %}{{ alert(warning, 'warning') }}{% endfor %} {% if not sysops_available %} {{ alert('Systemhelfer nicht verfügbar -- Konfiguration und Dienstzustand können hier nur angezeigt, nicht geschrieben werden.', 'warning') }} {% endif %}
Dienst {{ service.get('active', 'unbekannt') }} Autostart: {{ service.get('enabled', '?') }}
Programm {{ service.get('binary', '?') }} kea-dhcp4
Reservierungen {{ reservations | length }} automatisch + manüll
Leases {{ leases | rejectattr('is_expired') | list | length }} gültig{% if leases | selectattr('is_expired') | list %}, {{ leases | selectattr('is_expired') | list | length }} abgelaufen{% endif %}
{% if current_user.has('dhcp.execute') %}
{{ icon('power', size=17) }} Dienst
Bewusst getrennt vom Speichern: eine abgelegte Konfiguration startet keinen Dienst.
{% for action, label, style, question in [ ('install', 'Kea installieren', '', 'Kea-DHCP-Server per apt installieren?'), ('start', 'Aktivieren und starten', 'primary', 'Dienst aktivieren und starten? Er verteilt danach IP-Adressen im Netz.'), ('stop', 'Stoppen und deaktivieren', 'danger', 'Dienst stoppen und deaktivieren? Clients erhalten danach keine Adressen mehr.')] %}
{% endfor %}
{% endif %} {# -- Subnetze ---------------------------------------------------------- #}
{{ icon('network', size=17) }} Subnetze
Subnetz und Maske kommen vom laufenden System -- eingegeben wird nur der Adressbereich.
{% if subnets %}
{% for subnet in subnets %} {% endfor %}
InterfaceBereichGatewayDNSAktiv
{{ subnet.interface }} {{ subnet.range_start }} – {{ subnet.range_end }} {{ subnet.gateway or '–' | safe }} {{ subnet.dns or '–' | safe }} {% if subnet.enabled %}{{ pill('aktiv', 'online') }}{% else %}{{ pill('pausiert', '') }}{% endif %} {% if current_user.has('dhcp.edit') %}
{% endif %}
{% else %} {{ empty_state('Kein Subnetz', 'Ohne Subnetz startet Kea nicht.', 'network') }} {% endif %} {% if current_user.has('dhcp.edit') %}
{{ field('range_start', 'Von', required=True, placeholder='192.168.1.100') }} {{ field('range_end', 'Bis', required=True, placeholder='192.168.1.200') }} {{ field('gateway', 'Gateway', value=default_gateway) }} {{ field('dns', 'DNS-Server', placeholder='192.168.1.1, 9.9.9.9') }} {{ field('comment', 'Kommentar') }}
{% endif %}
{# -- Globale Einstellungen --------------------------------------------- #} {% if current_user.has('dhcp.edit') %}
{{ icon('sliders', size=17) }} Globale Einstellungen
{{ field('dhcp_domain', 'Domainname', value=values.dhcp_domain) }} {{ field('dhcp_lease_default', 'Lease-Dauer (s)', value=values.dhcp_lease_default, type='number') }} {{ field('dhcp_lease_max', 'Maximale Lease-Dauer (s)', value=values.dhcp_lease_max, type='number') }} {{ field('dhcp_output_path', 'Zielpfad der Konfiguration', value=values.dhcp_output_path, extra_class='field--full') }}
{% endif %} {# -- Optionen ---------------------------------------------------------- #}
{{ icon('puzzle', size=17) }} DHCP-Optionen
{{ options | length }} Optionen · Standardoptionen sind nicht löschbar, ihr Wert lässt sich leeren
{{ search_box('options-table', 'Option suchen ...') }}
{% for option in options %} {% endfor %}
CodeNameTypWert
{{ option.code }}
{{ option.name }}
{{ option.description }}
{{ option.type }} {% if current_user.has('dhcp.edit') %}
{% else %} {{ option.value or '–' | safe }} {% endif %}
{% if current_user.has('dhcp.edit') and not option.is_standard %}
{% endif %}
{% if current_user.has('dhcp.edit') %}
{{ field('code', 'Code', type='number', required=True) }} {{ field('name', 'Name', required=True, placeholder='meine-option') }} {{ field('description', 'Beschreibung') }}
{# Der Wert einer Option gilt sonst für alle. Genau dafür gibt es ``dhcp_option_values.device_id``: ein eigener Wert je Gerät, der in dessen Reservierung geschrieben wird -- etwa eine Terminal-URL, die je Gerät anders lautet. Auswertbar war das von Anfang an, eingeben liess es sich nicht. #}
{{ icon('sliders', size=17) }} Option nur für ein Gerät
Der Wert landet in der Reservierung dieses Geräts und überschreibt dort den globalen Wert. Leeres Feld entfernt ihn wieder.
{{ field('value', 'Wert', extra_class='field--full', hint_text='Leer = Wert für dieses Gerät entfernen') }}
{% endif %}
{# -- Reservierungen und Leases ----------------------------------------- #}
{{ icon('credit-card', size=17) }} Reservierungen
Automatisch aus dem Gerätebestand; bei MAC-Kollision gewinnt der Gerätebestand. Nur Adressen aus eingerichteten Bereichen.
{% if skipped_reservations %} {# Nicht verschweigen, sondern benennen: vorher standen diese Einträge in der Liste, obwohl Kea sie nie ausgeliefert hat. #}
{{ alert(skipped_reservations | length ~ ' Gerät(e) liegen in keinem eingerichteten Bereich und bekommen keine Reservierung. Legen Sie den passenden Bereich an, wenn sie eine bekommen sollen.', 'warning') }}
Welche?
    {% for entry in skipped_reservations %}
  • {{ entry.ip }} – {{ entry.name }} ({{ entry.source }})
  • {% endfor %}
{% endif %}
{% for reservation in reservations %} {% endfor %}
MACIPHostnameZustandQuelle Eigene Optionen
{{ reservation.mac }} {{ reservation.ip }} {{ reservation.hostname }} {{ pill(reservation.state | replace('_', ' '), reservation.state_style) }} {% if reservation.state == 'andere_adresse' and reservation.lease %}
jetzt {{ reservation.lease.address }}
{% elif reservation.state == 'aktiv' and reservation.lease %}
{{ reservation.lease.remaining_text }}
{% endif %}
{{ pill(reservation.source, 'accent' if reservation.source == 'auto' else '') }} {% set eigene = device_option_values.get(reservation.device_id) if reservation.device_id else none %} {% if eigene %} {% for wert in eigene %}
{{ wert.code }} {{ wert.name }} = {{ wert.value }}
{% endfor %} {% else %} {% endif %}
{% if reservation.source == 'manüll' and current_user.has('dhcp.edit') %}
{% endif %}
{% if current_user.has('dhcp.edit') %}
{{ field('mac', 'MAC', required=True, placeholder='aa:bb:cc:dd:ee:ff') }} {{ field('ip', 'IP', required=True) }} {{ field('hostname', 'Hostname') }} {{ field('comment', 'Kommentar') }}
{% endif %}
{{ icon('clock', size=17) }} Leases
Direkt aus {{ lease_file }} gelesen – je Adresse gilt der letzte Eintrag.
{% if leases and service.get('active') != 'active' %} {# Sonst sieht man alte Einträge und hält sie für den aktüllen Stand: ohne laufenden Dienst wird keine Lease erneuert, und die Datei veraltet still. #}
{{ alert('Der DHCP-Dienst läuft nicht (' ~ service.get('active', 'unbekannt') ~ '). Es wird keine Lease erneuert -- die Liste zeigt den letzten Stand der Datei, nicht die aktülle Lage. Der Grund steht im Kea-Protokoll.', 'warning') }}
{% endif %} {% if leases %}
{% for lease in leases %} {% endfor %}
IPMACHostnameArtZustand Läuft ab
{{ lease.address }} {{ lease.mac }} {{ lease.hostname or '–' | safe }} {% if lease.address in reserved_ips %}{{ pill('Reservierung', 'accent') }} {% else %}{{ pill('dynamisch', '') }}{% endif %} {% if lease.is_expired %}{{ pill('abgelaufen', 'warning') }} {% elif lease.is_active %}{{ pill('gültig', 'online') }} {% else %}{{ pill('inaktiv', '') }}{% endif %} {{ lease.expires_text }}
{{ lease.remaining_text }}
{% else %} {{ empty_state('Keine Leases', 'Es sind keine vergebenen Adressen bekannt.', 'clock') }} {% endif %}
{# -- Vorschau ---------------------------------------------------------- #}
{{ icon('file-text', size=17) }} Erzeugte Kea-Konfiguration
Vorschau – so würde die Datei geschrieben
{{ icon('chevron-down', size=16) }}
{{ preview }}
{% if current_user.has('dhcp.edit') %} {% endif %}
{% endblock %}