feat(settings): Settings brought into line with the rest of Steward (milestone 493)
The last page of #5214's walk. Settings still described Steward as it was before monitors were unified and SNMP was rebuilt, drew its tabs below the breadcrumb, and showed the report schedule in UTC. - Tabs (#5377): settings/_tabs.html is a settings_tabs() macro called in each page's section_tabs block, above the trail, as Ansible's are. The breadcrumb guard also fails any page that {% include %}s a tab strip. - Report time (#5378): reports.schedule_tz keeps the zone of whoever saved the schedule; check_and_send compares in it (report_due), and the form shows the day and hour on the reader's clock (schedule_in) under their zone's name. "Last sent" uses |when. - Wording (#5379): the report contents list what the report holds (monitors, not ping-enabled hosts); "Ping latency" is "Response time" and says what it grades; plugin detail says enabling loads now and disabling needs a restart; the monitor interval says it applies on the next check (the scheduler reads it every pass); the dead list-config branch is gone. - Traefik (#5380): the 5xx and 4xx cutoffs are settings (traefik.threshold.error_5xx_pct 0.1, error_4xx_pct 1.0) in a "Traefik routers" group on Thresholds while the plugin runs, read by router_state through error_cutoffs(). The Traefik page has a Thresholds button for admins, to /settings/thresholds/#traefik. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+19
-1
@@ -213,8 +213,25 @@ async def send_report(app) -> tuple[bool, str]:
|
||||
return False, f"Send failed: {exc}"
|
||||
|
||||
|
||||
def report_due(now: datetime, day: int, hour: int, tz) -> bool:
|
||||
"""Whether `now` falls in the scheduled hour, read in the schedule's zone."""
|
||||
local = now.astimezone(tz)
|
||||
return local.weekday() == day and local.hour == hour
|
||||
|
||||
|
||||
def schedule_in(day: int, hour: int, saved_tz, view_tz, now: datetime | None = None) -> tuple[int, int]:
|
||||
"""The schedule's (weekday, hour) as a reader in `view_tz` sees it: the next
|
||||
time it falls due in `saved_tz`, read on their clock."""
|
||||
local = (now or datetime.now(timezone.utc)).astimezone(saved_tz)
|
||||
due = (local + timedelta(days=(day - local.weekday()) % 7)).replace(
|
||||
hour=hour, minute=0, second=0, microsecond=0)
|
||||
seen = due.astimezone(view_tz)
|
||||
return seen.weekday(), seen.hour
|
||||
|
||||
|
||||
async def check_and_send(app) -> None:
|
||||
"""Hourly scheduler hook: send if day/hour match and not sent recently."""
|
||||
from steward.core.formatting import zone
|
||||
from steward.core.settings import get_setting, set_setting
|
||||
|
||||
async with app.db_sessionmaker() as db:
|
||||
@@ -224,10 +241,11 @@ async def check_and_send(app) -> None:
|
||||
|
||||
schedule_day = int(await get_setting(db, "reports.schedule_day") or 6)
|
||||
schedule_hour = int(await get_setting(db, "reports.schedule_hour") or 8)
|
||||
schedule_tz = zone(await get_setting(db, "reports.schedule_tz"))
|
||||
last_sent_str = await get_setting(db, "reports.last_sent_at") or ""
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
if now.weekday() != schedule_day or now.hour != schedule_hour:
|
||||
if not report_due(now, schedule_day, schedule_hour, schedule_tz):
|
||||
return
|
||||
|
||||
if last_sent_str:
|
||||
|
||||
@@ -83,6 +83,10 @@ DEFAULTS: dict[str, Any] = {
|
||||
"ansible.max_concurrent_runs": 3,
|
||||
"ping.threshold.good_ms": 50,
|
||||
"ping.threshold.warn_ms": 200,
|
||||
# A Traefik router serving more than this share of 5xx is failing; more
|
||||
# than this share of 4xx is worth a look (#5380).
|
||||
"traefik.threshold.error_5xx_pct": 0.1,
|
||||
"traefik.threshold.error_4xx_pct": 1.0,
|
||||
# Degraded/critical cutoffs for metric coloring (warn=amber, crit=red).
|
||||
# cpu/mem/disk/load are percentages (load is load-per-core %); temp in °C.
|
||||
# uptime is "higher is better" so its warn/crit are floors. Ping latency
|
||||
@@ -156,7 +160,10 @@ DEFAULTS: dict[str, Any] = {
|
||||
# Scheduled reports
|
||||
"reports.enabled": False,
|
||||
"reports.schedule_day": 6, # 0=Monday … 6=Sunday
|
||||
"reports.schedule_hour": 8, # UTC hour
|
||||
"reports.schedule_hour": 8, # an hour in reports.schedule_tz
|
||||
# The zone of whoever last saved the schedule (an IANA name; blank is UTC),
|
||||
# so "Sunday 08:00" means 08:00 where they are (#5378).
|
||||
"reports.schedule_tz": "",
|
||||
"reports.last_sent_at": "",
|
||||
}
|
||||
|
||||
@@ -452,6 +459,11 @@ def to_thresholds_cfg(settings: dict[str, Any]) -> dict:
|
||||
"tls_days": {"warn": g("thresholds.tls_warn_days", 14), "crit": g("thresholds.tls_crit_days", 3), "dir": "low"},
|
||||
"clock": {"warn": g("thresholds.clock_warn_s", 30), "crit": g("thresholds.clock_crit_s", 300), "dir": "high"},
|
||||
"crash_loop": {"warn": g("thresholds.crash_loop_deaths", 3), "crit": g("thresholds.crash_loop_deaths", 3), "dir": "high"},
|
||||
# One cutoff each: a router's 5xx share past it is crit, a 4xx share warn.
|
||||
"error_5xx": {"warn": g("traefik.threshold.error_5xx_pct", 0.1),
|
||||
"crit": g("traefik.threshold.error_5xx_pct", 0.1), "dir": "high"},
|
||||
"error_4xx": {"warn": g("traefik.threshold.error_4xx_pct", 1.0),
|
||||
"crit": g("traefik.threshold.error_4xx_pct", 1.0), "dir": "high"},
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user