Development

This commit is contained in:
Matthew Grotke 2026-05-28 01:42:31 -04:00
parent 433c9d12a5
commit c0a0e3daa9
3 changed files with 115 additions and 0 deletions

View file

@ -96,6 +96,47 @@ def addvlan_add():
ident['hostname'] = clean_hostname ident['hostname'] = clean_hostname
new_identities.append(ident) new_identities.append(ident)
identity_ips = [ident['ip'] for ident in new_identities]
gateway_raw = sanitize.ip(request.form.get('gateway', ''))
if gateway_raw and gateway_raw not in identity_ips:
flash(f"Gateway '{gateway_raw}' must match one of the server identity IPs.", 'error')
return redirect(f'/{_PAGE}')
inferred_gw = (min(identity_ips, key=lambda ip: int(ip.split('.')[-1])) if identity_ips else '')
new_stored_gw = gateway_raw if (gateway_raw and gateway_raw != inferred_gw) else ''
dns_override = 'dns_server_override' in request.form
dns_ips = []
for _line in request.form.get('dns_server', '').splitlines():
_line = _line.strip()
if not _line:
continue
_clean = sanitize.ip(_line)
if not _clean:
flash(f"'{_line}' is not a valid DNS server IP.", 'error')
return redirect(f'/{_PAGE}')
dns_ips.append(_clean)
new_stored_dns = dns_ips if dns_override 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(f'/{_PAGE}')
ntp_ips.append(_clean)
new_stored_ntp = ntp_ips if ntp_override else []
dhcp_domain_raw = request.form.get('dhcp_domain', '').strip()
dhcp_domain = sanitize.hostname(dhcp_domain_raw) if dhcp_domain_raw else 'local'
if dhcp_domain_raw and dhcp_domain is None:
flash(f"'{dhcp_domain_raw}' is not a valid domain name.", 'error')
return redirect(f'/{_PAGE}')
cfg = load_config() cfg = load_config()
vlans = cfg.setdefault('vlans', []) vlans = cfg.setdefault('vlans', [])
@ -106,6 +147,19 @@ def addvlan_add():
flash('Only one VLAN can be the RADIUS default.', 'error') flash('Only one VLAN can be the RADIUS default.', 'error')
return redirect(f'/{_PAGE}') return redirect(f'/{_PAGE}')
dhcp_info = {}
if dhcp_domain and dhcp_domain != 'local':
dhcp_info['domain'] = dhcp_domain
dhcp_overrides = {}
if new_stored_gw:
dhcp_overrides['gateway'] = new_stored_gw
if new_stored_dns:
dhcp_overrides['dns_server'] = new_stored_dns
if new_stored_ntp:
dhcp_overrides['ntp_server'] = new_stored_ntp
if dhcp_overrides:
dhcp_info['explicit_overrides'] = dhcp_overrides
entry = { entry = {
'name': name, 'name': name,
'vlan_id': vlan_id, 'vlan_id': vlan_id,
@ -118,6 +172,8 @@ def addvlan_add():
'mdns_reflection': mdns_reflection, 'mdns_reflection': mdns_reflection,
'server_identities': new_identities, 'server_identities': new_identities,
} }
if dhcp_info:
entry['dhcp_information'] = dhcp_info
if is_vpn: if is_vpn:
entry['peers'] = [] entry['peers'] = []
else: else:

View file

@ -254,6 +254,37 @@
"type": "p", "type": "p",
"text": "Servers provided to network clients via DHCP" "text": "Servers provided to network clients via DHCP"
}, },
{
"type": "field_row",
"cols": 4,
"items": [
{
"type": "dhcp_gateway_select",
"label": "Gateway",
"name": "gateway"
},
{
"type": "dhcp_override_textarea",
"label": "DNS Server(s)",
"name": "dns_server",
"override_name": "dns_server_override"
},
{
"type": "dhcp_override_textarea",
"label": "NTP Server(s)",
"name": "ntp_server",
"override_name": "ntp_server_override"
},
{
"type": "field",
"label": "Domain",
"name": "dhcp_domain",
"input_type": "text",
"validate": "networkname",
"value": "local"
}
]
},
{ {
"type": "hr" "type": "hr"
}, },

View file

@ -1276,6 +1276,34 @@ def _render_item(item, tokens, inherited_req=None):
f'</div>' f'</div>'
) )
if t == 'dhcp_gateway_select':
label = e(item.get('label', 'Gateway'))
name = e(item.get('name', 'gateway'))
return (
f'<div class="form-group">'
f'<label class="form-label">{label}</label>'
f'<select name="{name}" class="form-select dhcp-gateway-select" disabled>'
f'<option value="">— add identities first —</option>'
f'</select>'
f'</div>'
)
if t == 'dhcp_override_textarea':
label = e(item.get('label', ''))
name = e(item.get('name', ''))
override_name = e(item.get('override_name', name + '_override'))
return (
f'<div class="form-group">'
f'<label class="form-label dhcp-override-header">'
f'<span>{label}</span>'
f'<label class="dhcp-override-toggle">'
f'<input type="checkbox" name="{override_name}" class="form-checkbox dhcp-override-check"/> Override'
f'</label>'
f'</label>'
f'<textarea name="{name}" class="form-input dhcp-auto-textarea" rows="2" readonly></textarea>'
f'</div>'
)
if t == 'field': if t == 'field':
return _render_field(item, tokens) return _render_field(item, tokens)