Development

This commit is contained in:
Matthew Grotke 2026-05-27 04:21:09 -04:00
parent 08a0825345
commit f257070095
3 changed files with 59 additions and 15 deletions

View file

@ -1,4 +1,5 @@
import copy import copy
import ipaddress
from flask import Blueprint, request, redirect, flash from flask import Blueprint, request, redirect, flash
from auth import require_level from auth import require_level
@ -209,18 +210,52 @@ def networklayout_tablevlans_edit():
existing_gw = existing.get('dhcp_information', {}).get('explicit_overrides', {}).get('gateway', '') existing_gw = existing.get('dhcp_information', {}).get('explicit_overrides', {}).get('gateway', '')
dns_override = 'dns_server_override' in request.form dns_override = 'dns_server_override' in request.form
dns_raw = sanitize.ip(request.form.get('dns_server', '')) dns_ips = []
if dns_override: for _line in request.form.get('dns_server', '').splitlines():
if not dns_raw: _line = _line.strip()
flash('DNS server IP is required when override is enabled.', 'error') if not _line:
continue
_clean = sanitize.ip(_line)
if not _clean:
flash(f"'{_line}' is not a valid DNS server IP.", 'error')
return redirect(VIEW) return redirect(VIEW)
if dns_raw not in identity_ips: dns_ips.append(_clean)
flash(f"DNS server '{dns_raw}' must match one of the server identity IPs.", 'error') if dns_override and not dns_ips:
flash('At least one DNS server IP is required when override is enabled.', 'error')
return redirect(VIEW) return redirect(VIEW)
inferred_dns = (min(identity_ips, key=lambda ip: int(ip.split('.')[-1])) if dns_override and dns_ips:
if identity_ips else '') _vlan_net = ipaddress.IPv4Network(f'{subnet}/{final_mask}', strict=False)
new_stored_dns = dns_raw if (dns_override and dns_raw and dns_raw != inferred_dns) else '' for _ip in dns_ips:
existing_dns = existing.get('dhcp_information', {}).get('explicit_overrides', {}).get('dns_server', '') if ipaddress.IPv4Address(_ip) not in _vlan_net:
flash(f"DNS server '{_ip}' is not in the VLAN subnet ({subnet}/{final_mask}).", 'error')
return redirect(VIEW)
new_stored_dns = dns_ips if dns_override else []
_existing_dns = existing.get('dhcp_information', {}).get('explicit_overrides', {}).get('dns_server', [])
existing_dns = _existing_dns if isinstance(_existing_dns, list) else ([_existing_dns] if _existing_dns else [])
ntp_override = 'ntp_server_override' in request.form
ntp_ips = []
for _line in request.form.get('ntp_server', '').splitlines():
_line = _line.strip()
if not _line:
continue
_clean = sanitize.ip(_line)
if not _clean:
flash(f"'{_line}' is not a valid NTP server IP.", 'error')
return redirect(VIEW)
ntp_ips.append(_clean)
if ntp_override and not ntp_ips:
flash('At least one NTP server IP is required when override is enabled.', 'error')
return redirect(VIEW)
if ntp_override and ntp_ips:
_vlan_net = ipaddress.IPv4Network(f'{subnet}/{final_mask}', strict=False)
for _ip in ntp_ips:
if ipaddress.IPv4Address(_ip) not in _vlan_net:
flash(f"NTP server '{_ip}' is not in the VLAN subnet ({subnet}/{final_mask}).", 'error')
return redirect(VIEW)
new_stored_ntp = ntp_ips if ntp_override else []
_existing_ntp = existing.get('dhcp_information', {}).get('explicit_overrides', {}).get('ntp_server', [])
existing_ntp = _existing_ntp if isinstance(_existing_ntp, list) else ([_existing_ntp] if _existing_ntp else [])
_ids_unchanged = ( _ids_unchanged = (
len(new_identities) == len(old_identities) and len(new_identities) == len(old_identities) and
@ -240,7 +275,8 @@ def networklayout_tablevlans_edit():
and sorted(use_blocklists) == sorted(existing.get('use_blocklists', [])) and sorted(use_blocklists) == sorted(existing.get('use_blocklists', []))
and _ids_unchanged and _ids_unchanged
and new_stored_gw == existing_gw and new_stored_gw == existing_gw
and new_stored_dns == existing_dns): and new_stored_dns == existing_dns
and new_stored_ntp == existing_ntp):
flash('No changes were made.', 'info') flash('No changes were made.', 'info')
return redirect(VIEW) return redirect(VIEW)
@ -265,6 +301,12 @@ def networklayout_tablevlans_edit():
dhcp_overrides['dns_server'] = new_stored_dns dhcp_overrides['dns_server'] = new_stored_dns
else: else:
dhcp_overrides.pop('dns_server', None) dhcp_overrides.pop('dns_server', None)
if new_stored_ntp:
dhcp_overrides['ntp_server'] = new_stored_ntp
else:
dhcp_overrides.pop('ntp_server', None)
if not dhcp_overrides:
existing.get('dhcp_information', {}).pop('explicit_overrides', None)
errors = validate.validate_config(cfg) errors = validate.validate_config(cfg)
if errors: if errors:
for msg in errors: for msg in errors:

View file

@ -325,9 +325,10 @@ def _config_datasource(name):
row['server_identity_gateway'] = ( row['server_identity_gateway'] = (
v.get('dhcp_information', {}).get('explicit_overrides', {}).get('gateway', '') v.get('dhcp_information', {}).get('explicit_overrides', {}).get('gateway', '')
) )
row['server_identity_dns_server'] = ( _dns = v.get('dhcp_information', {}).get('explicit_overrides', {}).get('dns_server', [])
v.get('dhcp_information', {}).get('explicit_overrides', {}).get('dns_server', '') row['server_identity_dns_server'] = '\n'.join(_dns) if isinstance(_dns, list) else str(_dns or '')
) _ntp = v.get('dhcp_information', {}).get('explicit_overrides', {}).get('ntp_server', [])
row['server_identity_ntp_server'] = '\n'.join(_ntp) if isinstance(_ntp, list) else str(_ntp or '')
rows.append(row) rows.append(row)
return rows return rows

View file

@ -1589,7 +1589,8 @@
"pair_label2": "Hostname (Opt)", "pair_label2": "Hostname (Opt)",
"pair_validate2": "networkname", "pair_validate2": "networkname",
"gateway_col": "server_identity_gateway", "gateway_col": "server_identity_gateway",
"dns_col": "server_identity_dns_server" "dns_col": "server_identity_dns_server",
"ntp_col": "server_identity_ntp_server"
}, },
{ {
"col": "radius_default", "col": "radius_default",