from pathlib import Path import copy from flask import Blueprint, request, redirect, flash from auth import require_level from config_utils import load_config, record_group, diff_fields, verify_config_hash import sanitize import mod_validation as validate _PAGE = Path(__file__).parent.name bp = Blueprint(_PAGE, __name__) def _row_index(): try: return int(request.form.get('row_index', '')) except (ValueError, TypeError): return None def _hash_ok(): if not verify_config_hash(request.form.get('config_hash', '')): flash('Configuration was modified by another session. Please refresh and try again.', 'error') return False return True @bp.route('/action/hostoverrides/addoverride_add', methods=['POST']) @require_level('administrator') def addoverride_add(): description = sanitize.text(request.form.get('description', '')) host = validate.domainname(request.form.get('host', '')) ip = sanitize.ip(request.form.get('ip', '')) if not host or not ip: flash('Hostname and IP address are required.', 'error') return redirect(f'/{_PAGE}') if not _hash_ok(): return redirect(f'/{_PAGE}') cfg = load_config() err = validate.check_host_override_ip_in_vlans(ip, cfg) if err: flash(err, 'error') return redirect(f'/{_PAGE}') entry = {'description': description, 'host': host, 'ip': ip, 'enabled': True} cfg.setdefault('host_overrides', []).append(entry) errors = validate.validate_config(cfg) if errors: for msg in errors: flash(msg, 'error') return redirect(f'/{_PAGE}') changes = diff_fields(None, entry) flash(record_group(cfg, 'host_overrides', 'host', host, changes, 'core apply'), 'success') return redirect(f'/{_PAGE}') @bp.route('/action/hostoverrides/table_toggle', methods=['POST']) @require_level('administrator') def table_toggle(): idx = _row_index() if idx is None: flash('Invalid request.', 'error') return redirect(f'/{_PAGE}') if not _hash_ok(): return redirect(f'/{_PAGE}') cfg = load_config() items = cfg.get('host_overrides', []) if idx < 0 or idx >= len(items): flash('Entry not found.', 'error') return redirect(f'/{_PAGE}') old_enabled = items[idx].get('enabled', True) before = copy.deepcopy(items[idx]) items[idx]['enabled'] = not old_enabled errors = validate.validate_config(cfg) if errors: for msg in errors: flash(msg, 'error') return redirect(f'/{_PAGE}') host = items[idx]['host'] changes = diff_fields(before, items[idx]) flash(record_group(cfg, 'host_overrides', 'host', host, changes, 'core apply'), 'success') return redirect(f'/{_PAGE}') @bp.route('/action/hostoverrides/table_edit', methods=['POST']) @require_level('administrator') def table_edit(): idx = _row_index() if idx is None: flash('Invalid request.', 'error') return redirect(f'/{_PAGE}') description = sanitize.text(request.form.get('description', '')) host = validate.domainname(request.form.get('host', '')) ip = sanitize.ip(request.form.get('ip', '')) enabled = request.form.get('enabled') == 'on' if not host or not ip: flash('Hostname and IP address are required.', 'error') return redirect(f'/{_PAGE}') if not _hash_ok(): return redirect(f'/{_PAGE}') cfg = load_config() err = validate.check_host_override_ip_in_vlans(ip, cfg) if err: flash(err, 'error') return redirect(f'/{_PAGE}') items = cfg.get('host_overrides', []) if idx < 0 or idx >= len(items): flash('Entry not found.', 'error') return redirect(f'/{_PAGE}') before = copy.deepcopy(items[idx]) items[idx].update({'description': description, 'host': host, 'ip': ip, 'enabled': enabled}) errors = validate.validate_config(cfg) if errors: for msg in errors: flash(msg, 'error') return redirect(f'/{_PAGE}') changes = diff_fields(before, items[idx]) flash(record_group(cfg, 'host_overrides', 'host', host, changes, 'core apply'), 'success') return redirect(f'/{_PAGE}') @bp.route('/action/hostoverrides/table_delete', methods=['POST']) @require_level('administrator') def table_delete(): idx = _row_index() if idx is None: flash('Invalid request.', 'error') return redirect(f'/{_PAGE}') if not _hash_ok(): return redirect(f'/{_PAGE}') cfg = load_config() items = cfg.get('host_overrides', []) if idx < 0 or idx >= len(items): flash('Entry not found.', 'error') return redirect(f'/{_PAGE}') removed = items.pop(idx) errors = validate.validate_config(cfg) if errors: for msg in errors: flash(msg, 'error') return redirect(f'/{_PAGE}') changes = diff_fields(removed, None) flash(record_group(cfg, 'host_overrides', 'host', removed['host'], changes, 'core apply'), 'success') return redirect(f'/{_PAGE}')