Development

This commit is contained in:
Matthew Grotke 2026-05-27 22:04:04 -04:00
parent eed1d295dc
commit d9f3bd8289
45 changed files with 635 additions and 666 deletions

View file

@ -1,3 +1,4 @@
from pathlib import Path
import copy
import os
@ -7,9 +8,9 @@ from config_utils import load_config, save_config_with_snapshot, verify_config_h
import sanitize
import validation as validate
bp = Blueprint('physicalinterfaces', __name__)
_PAGE = Path(__file__).parent.name
_VIEW = '/view/view_physicalinterfaces'
bp = Blueprint(_PAGE, __name__)
_EXCLUDE_PREFIXES = ('lo', 'wg', 'docker', 'br-', 'veth',
'tun', 'tap', 'ppp', 'virbr',
@ -31,29 +32,29 @@ def _valid_interface(name):
return name in _get_system_interfaces()
@bp.route('/action/physicalinterfaces_cardphysicalinterface_save', methods=['POST'])
@bp.route('/action/physicalinterfaces/physicalinterface_save', methods=['POST'])
@require_level('administrator')
def physicalinterfaces_cardphysicalinterface_save():
def physicalinterface_save():
wan = sanitize.interface_name(request.form.get('wan_interface', ''))
lan = sanitize.interface_name(request.form.get('lan_interface', ''))
if not wan or not lan:
flash('Both WAN and LAN interfaces are required.', 'error')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
if wan == lan:
flash('WAN and LAN interfaces must be different.', 'error')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
if not verify_config_hash(request.form.get('config_hash', '')):
flash('Configuration was modified by another session. Please refresh and try again.', 'error')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
available = _get_system_interfaces()
for iface in (wan, lan):
if available and iface not in available:
flash(f"Interface '{iface}' does not exist on this system.", 'error')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
cfg = load_config()
before = copy.deepcopy(cfg.get('network_interfaces', {}))
@ -64,22 +65,22 @@ def physicalinterfaces_cardphysicalinterface_save():
if errors:
for msg in errors:
flash(msg, 'error')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
flash(save_config_with_snapshot(
cfg, path='network_interfaces', key='global', operation='edit',
before=before, after=copy.deepcopy(cfg['network_interfaces']),
description='Updated network interfaces',
cmd='core apply',
), 'success')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
@bp.route('/action/physicalinterfaces_cardinterfaceconfiguration_apply', methods=['POST'])
@bp.route('/action/physicalinterfaces/ifaceconfig_apply', methods=['POST'])
@require_level('administrator')
def physicalinterfaces_cardinterfaceconfiguration_apply():
def ifaceconfig_apply():
if not verify_config_hash(request.form.get('config_hash', '')):
flash('Configuration was modified by another session. Please refresh and try again.', 'error')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
iface = sanitize.interface_name(request.form.get('iface', ''))
mtu = request.form.get('mtu', '').strip()
@ -89,27 +90,27 @@ def physicalinterfaces_cardinterfaceconfiguration_apply():
if not iface:
flash('No interface specified.', 'error')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
if not _valid_interface(iface):
flash(f"Interface '{iface}' does not exist on this system.", 'error')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
mtu_int = None
if mtu:
mtu_int = validate.int_range(mtu, 68, 9000)
if mtu_int is None:
flash('MTU must be an integer between 68 and 9000.', 'error')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
mac_raw = request.form.get('mac', '').strip()
if mac_raw and not mac:
flash('MAC address must be in the format aa:bb:cc:dd:ee:ff.', 'error')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
if not mtu_int and not mac:
flash('No changes specified.', 'error')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
queued = False
if mtu_int and str(mtu_int) != original_mtu:
@ -121,7 +122,7 @@ def physicalinterfaces_cardinterfaceconfiguration_apply():
if not queued:
flash('No changes detected.', 'info')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')
flash(queued_msg(action_label='Changes queued'), 'success')
return redirect(_VIEW)
return redirect(f'/{_PAGE}')

View file

@ -1,5 +1,4 @@
{
"id": "view_physicalinterfaces",
"client_requirement": "client_is_administrator+",
"items": [
{
@ -22,7 +21,7 @@
"items": [
{
"type": "form",
"action": "/action/physicalinterfaces_cardphysicalinterface_save",
"action": "/action/physicalinterfaces/physicalinterface_save",
"method": "post",
"items": [
{
@ -46,7 +45,7 @@
"items": [
{
"type": "button_primary",
"action": "/action/physicalinterfaces_cardphysicalinterface_save",
"action": "/action/physicalinterfaces/physicalinterface_save",
"method": "post",
"text": "Save"
},
@ -69,7 +68,7 @@
"items": [
{
"type": "form",
"action": "/action/physicalinterfaces_cardinterfaceconfiguration_apply",
"action": "/action/physicalinterfaces/ifaceconfig_apply",
"method": "post",
"items": [
{
@ -142,7 +141,7 @@
"items": [
{
"type": "button_primary",
"action": "/action/physicalinterfaces_cardinterfaceconfiguration_apply",
"action": "/action/physicalinterfaces/ifaceconfig_apply",
"method": "post",
"text": "Apply"
},