|
|
|
@@ -73,8 +73,8 @@ def _purge_old_jobs_loop():
|
|
|
|
|
for jid, j in list(_diag_jobs.items()):
|
|
|
|
|
if j['status'] == 'running' and j.get('created_at', 0) < stuck_cutoff:
|
|
|
|
|
j['status'] = 'done'
|
|
|
|
|
j['result'] = {'status': 'error', 'error': 'Diagnostic timed out (thread crash)'}
|
|
|
|
|
logger.error(f'Diagnostic job {jid} appeared stuck; marked as errored')
|
|
|
|
|
j['result'] = {'status': 'error', 'error': 'Diagnostic abandoned — no activity for 5 minutes.'}
|
|
|
|
|
logger.error(f'Diagnostic job {jid} stuck (no activity for 5 min); marked done/error')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_purge_thread = threading.Thread(target=_purge_old_jobs_loop, daemon=True)
|
|
|
|
@@ -143,12 +143,31 @@ def require_auth(f):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Page routes
|
|
|
|
|
# Helpers
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
_PAGE_LIMIT = 200 # max events returned per request
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _annotate_suppressions(events: list, suppressions: list) -> None:
|
|
|
|
|
"""Annotate each event dict in-place with an is_suppressed bool."""
|
|
|
|
|
for ev in events:
|
|
|
|
|
sup_type = (
|
|
|
|
|
'unifi_device' if ev.get('event_type') == 'unifi_device_offline'
|
|
|
|
|
else 'interface' if ev.get('event_type') == 'interface_down'
|
|
|
|
|
else 'host'
|
|
|
|
|
)
|
|
|
|
|
ev['is_suppressed'] = db.check_suppressed(
|
|
|
|
|
suppressions, sup_type,
|
|
|
|
|
ev.get('target_name', ''), ev.get('target_detail', '') or '',
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Page routes
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/')
|
|
|
|
|
@require_auth
|
|
|
|
|
def index():
|
|
|
|
@@ -160,16 +179,7 @@ def index():
|
|
|
|
|
last_check = db.get_state('last_check', 'Never')
|
|
|
|
|
snapshot = json.loads(snapshot_raw) if snapshot_raw else {}
|
|
|
|
|
suppressions = db.get_active_suppressions()
|
|
|
|
|
for ev in events:
|
|
|
|
|
sup_type = (
|
|
|
|
|
'unifi_device' if ev.get('event_type') == 'unifi_device_offline'
|
|
|
|
|
else 'interface' if ev.get('event_type') == 'interface_down'
|
|
|
|
|
else 'host'
|
|
|
|
|
)
|
|
|
|
|
ev['is_suppressed'] = db.check_suppressed(
|
|
|
|
|
suppressions, sup_type,
|
|
|
|
|
ev.get('target_name', ''), ev.get('target_detail', '') or '',
|
|
|
|
|
)
|
|
|
|
|
_annotate_suppressions(events, suppressions)
|
|
|
|
|
recent_resolved = db.get_recent_resolved(hours=24, limit=10)
|
|
|
|
|
return render_template(
|
|
|
|
|
'index.html',
|
|
|
|
@@ -225,16 +235,7 @@ def suppressions_page():
|
|
|
|
|
def api_status():
|
|
|
|
|
active = db.get_active_events(limit=_PAGE_LIMIT)
|
|
|
|
|
suppressions = db.get_active_suppressions()
|
|
|
|
|
for ev in active:
|
|
|
|
|
sup_type = (
|
|
|
|
|
'unifi_device' if ev.get('event_type') == 'unifi_device_offline'
|
|
|
|
|
else 'interface' if ev.get('event_type') == 'interface_down'
|
|
|
|
|
else 'host'
|
|
|
|
|
)
|
|
|
|
|
ev['is_suppressed'] = db.check_suppressed(
|
|
|
|
|
suppressions, sup_type,
|
|
|
|
|
ev.get('target_name', ''), ev.get('target_detail', '') or '',
|
|
|
|
|
)
|
|
|
|
|
_annotate_suppressions(active, suppressions)
|
|
|
|
|
last_check = db.get_state('last_check', 'Never')
|
|
|
|
|
return jsonify({
|
|
|
|
|
'summary': db.get_status_summary(),
|
|
|
|
|