From 2276c83443a17ece6441237ad4d9ff5aecd00516 Mon Sep 17 00:00:00 2001 From: Marcel Peterkau Date: Sat, 27 Dec 2025 09:52:09 +0100 Subject: [PATCH] =?UTF-8?q?Helfer=20f=C3=BCr=20Interface-Infos,=20MAC-Norm?= =?UTF-8?q?alisierung=20und=20Option-Spezifikationen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/option_specs.py | 18 ++++++++++++++++++ src/utils.py | 28 ++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 src/option_specs.py diff --git a/src/option_specs.py b/src/option_specs.py new file mode 100644 index 0000000..8f70d7e --- /dev/null +++ b/src/option_specs.py @@ -0,0 +1,18 @@ +""" +Definitions for supported extended DHCP options in the GUI. +Extend this list to add more options; codes must be numeric strings. +""" + +EXTENDED_OPTION_SPECS = [ + # Managed internally (anzeige, aber nicht editierbar) + {"code": "1", "label": "Subnet Mask", "desc": "Automatisch aus IP-Settings", "type": "text", "disabled": True}, + {"code": "3", "label": "Router/Gateway", "desc": "Automatisch auf Server-IP", "type": "text", "disabled": True}, + {"code": "6", "label": "DNS-Server", "desc": "Aus IP-Settings (GUI)", "type": "text", "disabled": True}, + + # Freie Optionen (aufsteigend) + {"code": "15", "label": "DNS-Suffix", "desc": "DNS-Domäne", "type": "text"}, + {"code": "42", "label": "NTP-Server", "desc": "NTP-Server IPs (kommagetrennt)", "type": "text"}, + {"code": "51", "label": "Lease Time", "desc": "Lease in Sekunden (mandatory)", "type": "text", "mandatory": True, "default": "3600"}, + {"code": "66", "label": "TFTP-Server", "desc": "TFTP-Servername oder IP", "type": "text"}, + {"code": "67", "label": "Bootfile", "desc": "Bootfile-Name (PXE)", "type": "text"}, +] diff --git a/src/utils.py b/src/utils.py index 355fa0d..52d6feb 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,8 +1,11 @@ -import socket import ipaddress -import psutil +import socket +import string from typing import Optional, Tuple +import psutil + + def get_network_interfaces(): """Return only interfaces that have an IPv4 address to keep choices sane.""" interfaces = [] @@ -21,6 +24,19 @@ def get_iface_ipv4_config(iface: str) -> Tuple[Optional[str], Optional[str]]: return a.address, a.netmask return None, None + +def get_iface_hwinfo(iface: str) -> Tuple[Optional[str], Optional[int]]: + """Return (MAC, speed_mbps) where available.""" + mac = None + addrs = psutil.net_if_addrs().get(iface, []) + for a in addrs: + if getattr(a, "family", None) == getattr(psutil, "AF_LINK", None): + mac = a.address + break + stats = psutil.net_if_stats().get(iface) + speed = stats.speed if stats else None + return mac, speed + def format_ip_address(ip: str) -> str: """Normalize dotted quad; raises on invalid input.""" ip_obj = ipaddress.ip_address(ip) @@ -31,3 +47,11 @@ def format_ip_address(ip: str) -> str: def validate_subnet_mask(mask: str) -> None: """Accept masks like 255.255.255.0; raise ValueError if invalid.""" ipaddress.IPv4Network(f"0.0.0.0/{mask}") + + +def normalize_mac(mac: str) -> str: + """Return MAC as AA:BB:CC:DD:EE:FF; raises on invalid input.""" + cleaned = mac.strip().replace("-", "").replace(":", "").replace(".", "").lower() + if len(cleaned) != 12 or any(c not in string.hexdigits for c in cleaned): + raise ValueError("Ungültige MAC-Adresse.") + return ":".join(cleaned[i:i+2] for i in range(0, 12, 2)).upper()