feat: replace YAML editor with form UI for threshold configurations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Andreas Wrede
2026-05-12 10:57:03 -04:00
parent 668a135e53
commit 1cefc2676e
4 changed files with 297 additions and 3 deletions
+76 -1
View File
@@ -25,6 +25,74 @@ logger = logging.getLogger(__name__)
eventlog = notify_mod.eventlog
def _build_threshold_configs_from_form(form_data: dict) -> dict:
"""Convert form-submitted flat threshold data to nested threshold_configs YAML structure.
Input: {config_name: {metric_path: {warning, critical, operator, hysteresis, enabled, count, display}}}
Output: {config_name: {thresholds: {plugin: {metric: {warning, critical, ...}}}}}
"""
result = {}
for config_name, metrics in form_data.items():
if not isinstance(metrics, dict):
continue
thresholds = {}
for metric_path, values in metrics.items():
_insert_threshold_metric(thresholds, metric_path, values)
result[config_name] = {"thresholds": thresholds}
return result
def _insert_threshold_metric(thresholds: dict, metric_path: str, values: dict) -> None:
"""Insert a single metric into the nested threshold YAML structure."""
if not isinstance(values, dict):
return
cfg = {}
op = values.get("operator", ">")
if op and op != ">":
cfg["operator"] = op
for key, cast in (("warning", float), ("critical", float), ("hysteresis", float)):
v = values.get(key)
if v is not None:
try:
cfg[key] = cast(v)
except (TypeError, ValueError):
pass
count = values.get("count")
if count is not None:
try:
cfg["count"] = int(count)
except (TypeError, ValueError):
pass
display = values.get("display", "")
if display:
cfg["display"] = display
if not values.get("enabled", True):
cfg["enabled"] = False
parts = metric_path.split(".", 2)
if len(parts) == 1:
# e.g. "rtt"
thresholds[metric_path] = cfg
elif len(parts) == 2:
plugin, metric = parts
thresholds.setdefault(plugin, {})[metric] = cfg
else:
plugin, intermediate, leaf = parts
thresholds.setdefault(plugin, {})
if plugin == "disk_monitor":
thresholds[plugin].setdefault("partitions", {}).setdefault(intermediate, {})[leaf] = cfg
elif plugin == "zfs_monitor":
thresholds[plugin].setdefault("pools", {}).setdefault(intermediate, {})[leaf] = cfg
else:
thresholds[plugin].setdefault(intermediate, {})[leaf] = cfg
def _render_template(html_str: str, **context) -> str:
tmpl = jinja2.Template(html_str)
return tmpl.render(**context)
@@ -1228,10 +1296,17 @@ async def start(
attrs.pop("client_secret", None)
data["oauth"] = new_oauth
for section in ("notification_channels", "thresholds", "dns"):
for section in ("notification_channels", "dns"):
if section in payload:
configio_mod.apply_yaml_section(data, section, payload[section])
if "thresholds" in payload:
tc = payload["thresholds"]
if isinstance(tc, str):
configio_mod.apply_yaml_section(data, "thresholds", tc)
elif isinstance(tc, dict):
data["threshold_configs"] = _build_threshold_configs_from_form(tc)
if "hosts" in payload:
h = payload["hosts"]
if isinstance(h, dict):