feat(settings): configurable monitoring thresholds
Move the hardcoded warn/crit cutoffs into Settings -> Thresholds (DB-backed, live, no restart). New thresholds.* keys + to_thresholds_cfg() + a threshold_style(value, kind) jinja global that reads them; latency reuses the existing ping good/warn keys, uptime is direction-aware (floors). Replace the _macros metric_style/uptime_style macros (now removed) with the global across Hosts-Overview, host_agent fleet + panel, Uptime/SLA widget, and the ping page uptime column — all now honor the configured cutoffs. Uptime keeps its green 'good' look when not degraded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ from steward.models.users import UserRole
|
||||
from steward.core.settings import (
|
||||
get_all_settings, set_setting,
|
||||
to_smtp_cfg, to_webhook_cfg, to_ansible_cfg, to_plugins_cfg,
|
||||
to_oidc_cfg, to_ldap_cfg,
|
||||
to_oidc_cfg, to_ldap_cfg, to_thresholds_cfg,
|
||||
)
|
||||
|
||||
settings_bp = Blueprint("settings", __name__, url_prefix="/settings")
|
||||
@@ -72,6 +72,7 @@ async def _reload_app_config() -> None:
|
||||
PLUGINS=to_plugins_cfg(fresh),
|
||||
OIDC=to_oidc_cfg(fresh),
|
||||
LDAP=to_ldap_cfg(fresh),
|
||||
THRESHOLDS=to_thresholds_cfg(fresh),
|
||||
)
|
||||
|
||||
|
||||
@@ -113,6 +114,49 @@ async def save_general():
|
||||
return redirect(url_for("settings.general"))
|
||||
|
||||
|
||||
# ── Monitoring thresholds ─────────────────────────────────────────────────────
|
||||
|
||||
# (form field, setting key, is_float) for every tunable cutoff.
|
||||
_THRESHOLD_FIELDS = [
|
||||
("cpu_warn", "thresholds.cpu_warn", False), ("cpu_crit", "thresholds.cpu_crit", False),
|
||||
("mem_warn", "thresholds.mem_warn", False), ("mem_crit", "thresholds.mem_crit", False),
|
||||
("disk_warn", "thresholds.disk_warn", False), ("disk_crit", "thresholds.disk_crit", False),
|
||||
("load_warn", "thresholds.load_warn", False), ("load_crit", "thresholds.load_crit", False),
|
||||
("temp_warn", "thresholds.temp_warn", False), ("temp_crit", "thresholds.temp_crit", False),
|
||||
("uptime_warn", "thresholds.uptime_warn", True), ("uptime_crit", "thresholds.uptime_crit", True),
|
||||
("latency_warn", "ping.threshold.good_ms", False), ("latency_crit", "ping.threshold.warn_ms", False),
|
||||
]
|
||||
|
||||
|
||||
@settings_bp.get("/thresholds/")
|
||||
@require_role(UserRole.admin)
|
||||
async def thresholds():
|
||||
async with current_app.db_sessionmaker() as db:
|
||||
settings = await get_all_settings(db)
|
||||
return await render_template("settings/thresholds.html", settings=settings)
|
||||
|
||||
|
||||
@settings_bp.post("/thresholds/")
|
||||
@require_role(UserRole.admin)
|
||||
async def save_thresholds():
|
||||
form = await request.form
|
||||
async with current_app.db_sessionmaker() as db:
|
||||
async with db.begin():
|
||||
for field, key, is_float in _THRESHOLD_FIELDS:
|
||||
raw = form.get(field, "")
|
||||
if raw == "":
|
||||
continue
|
||||
try:
|
||||
val = float(raw) if is_float else int(raw)
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
await set_setting(db, key, val)
|
||||
await _reload_app_config()
|
||||
await log_audit(current_app, session.get("user_id"), session.get("username", ""),
|
||||
"settings.saved", detail={"section": "thresholds"})
|
||||
return redirect(url_for("settings.thresholds"))
|
||||
|
||||
|
||||
# ── Notifications (SMTP + Webhook) ────────────────────────────────────────────
|
||||
|
||||
@settings_bp.get("/notifications/")
|
||||
|
||||
Reference in New Issue
Block a user