609bd78af2
Bring the dashboard in line with the unified host IA + improved displays. - New core "Hosts — Overview" widget (/hosts/overview/widget): one row per host combining monitor status (ping dot + latency, uptime 24h) with the agent glance (CPU / memory / disk root + stale flag), each row linking to the host hub. Reads agent data from the generic PluginMetric table via a core-safe _agent_overview_by_host helper (no host_agent import); freshness vs the plugin's stale window. The granular Ping/DNS/Uptime/Agent widgets stay. - Group the add-widget picker into Core monitors / Monitoring capabilities / Integrations (a `group` field on every WIDGET_REGISTRY entry + section headings in _edit_panels.html), matching the Settings → Plugins taxonomy. - Fix the agent fleet widget rows to link to the host hub (/hosts/<id>) instead of the old /plugins/host_agent/<id>/ page. Scribe #903. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
345 lines
12 KiB
Python
345 lines
12 KiB
Python
# steward/core/widgets.py
|
|
#
|
|
# Central widget registry. Each entry describes one widget type that can be
|
|
# placed on a dashboard. Plugin widgets declare which plugin must be enabled.
|
|
#
|
|
# "params" is a list of configurable parameters for the widget. Each param:
|
|
# key - form field name (stored in config_json)
|
|
# label - human-readable label
|
|
# type - "select" (only type currently supported)
|
|
# default - default value (int or str determines how it's parsed from the form)
|
|
# options - list of {value, label} dicts
|
|
from __future__ import annotations
|
|
|
|
WIDGET_REGISTRY: dict[str, dict] = {
|
|
"ping": {
|
|
"key": "ping",
|
|
"label": "Ping",
|
|
"description": "Live ping status and latency history for all monitored hosts",
|
|
"hx_url": "/ping/rows",
|
|
"detail_url": "/ping/",
|
|
"plugin": None,
|
|
"poll": True,
|
|
"params": [],
|
|
},
|
|
"status_overview": {
|
|
"key": "status_overview",
|
|
"label": "Status — Overview",
|
|
"description": "Unified up/down/pending summary across ping, DNS, and HTTP monitors",
|
|
"hx_url": "/status/widget",
|
|
"detail_url": "/status",
|
|
"plugin": None,
|
|
"poll": True,
|
|
"params": [],
|
|
},
|
|
"hosts_overview": {
|
|
"key": "hosts_overview",
|
|
"label": "Hosts — Overview",
|
|
"description": "One row per host: monitor status + uptime and agent CPU/memory/disk, linking to the host hub",
|
|
"hx_url": "/hosts/overview/widget",
|
|
"detail_url": "/hosts/",
|
|
"plugin": None,
|
|
"poll": True,
|
|
"params": [],
|
|
},
|
|
"uptime_summary": {
|
|
"key": "uptime_summary",
|
|
"label": "Uptime / SLA",
|
|
"description": "Per-host rolling uptime % for 24h, 7d, and 30d windows",
|
|
"hx_url": "/hosts/uptime/widget",
|
|
"detail_url": "/hosts/uptime",
|
|
"plugin": None,
|
|
"poll": False,
|
|
"params": [],
|
|
},
|
|
"dns": {
|
|
"key": "dns",
|
|
"label": "DNS",
|
|
"description": "DNS resolution status for all monitored hosts",
|
|
"hx_url": "/dns/rows",
|
|
"detail_url": "/dns/",
|
|
"plugin": None,
|
|
"poll": True,
|
|
"params": [],
|
|
},
|
|
"traefik_routers": {
|
|
"key": "traefik_routers",
|
|
"label": "Traefik — Routers",
|
|
"description": "Top routers by request rate with error highlights and expiring certs",
|
|
"hx_url": "/plugins/traefik/widget",
|
|
"detail_url": "/plugins/traefik/",
|
|
"plugin": "traefik",
|
|
"poll": True,
|
|
"params": [],
|
|
},
|
|
"traefik_access_log": {
|
|
"key": "traefik_access_log",
|
|
"label": "Traefik — Access Log",
|
|
"description": "Traffic origin summary — top IPs, countries, and request distribution",
|
|
"hx_url": "/plugins/traefik/widget/access_log",
|
|
"detail_url": "/plugins/traefik/",
|
|
"plugin": "traefik",
|
|
"poll": True,
|
|
"params": [
|
|
{
|
|
"key": "ip_filter",
|
|
"label": "IP Filter",
|
|
"type": "select",
|
|
"default": "all",
|
|
"options": [
|
|
{"value": "all", "label": "All traffic"},
|
|
{"value": "internal", "label": "Internal only"},
|
|
{"value": "external", "label": "External only"},
|
|
],
|
|
},
|
|
{
|
|
"key": "limit",
|
|
"label": "Top IPs shown",
|
|
"type": "select",
|
|
"default": 10,
|
|
"options": [
|
|
{"value": 10, "label": "10 entries"},
|
|
{"value": 20, "label": "20 entries"},
|
|
{"value": 50, "label": "50 entries"},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"traefik_request_chart": {
|
|
"key": "traefik_request_chart",
|
|
"label": "Traefik — Request Chart",
|
|
"description": "Request rate and error percentage over time (Chart.js)",
|
|
"hx_url": "/plugins/traefik/widget/request_chart",
|
|
"detail_url": "/plugins/traefik/",
|
|
"plugin": "traefik",
|
|
"poll": True,
|
|
"params": [
|
|
{
|
|
"key": "hours",
|
|
"label": "Time range",
|
|
"type": "select",
|
|
"default": 6,
|
|
"options": [
|
|
{"value": 1, "label": "Last 1 hour"},
|
|
{"value": 6, "label": "Last 6 hours"},
|
|
{"value": 24, "label": "Last 24 hours"},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"unifi_overview": {
|
|
"key": "unifi_overview",
|
|
"label": "UniFi — Overview",
|
|
"description": "WAN status, client counts, offline devices, and active alarms",
|
|
"hx_url": "/plugins/unifi/widget",
|
|
"detail_url": "/plugins/unifi/",
|
|
"plugin": "unifi",
|
|
"poll": True,
|
|
"params": [],
|
|
},
|
|
"unifi_clients": {
|
|
"key": "unifi_clients",
|
|
"label": "UniFi — Top Clients",
|
|
"description": "Active wireless and wired clients sorted by bandwidth",
|
|
"hx_url": "/plugins/unifi/widget/clients",
|
|
"detail_url": "/plugins/unifi/",
|
|
"plugin": "unifi",
|
|
"poll": True,
|
|
"params": [
|
|
{
|
|
"key": "limit",
|
|
"label": "Clients shown",
|
|
"type": "select",
|
|
"default": 5,
|
|
"options": [
|
|
{"value": 5, "label": "5 clients"},
|
|
{"value": 10, "label": "10 clients"},
|
|
{"value": 20, "label": "20 clients"},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"unifi_devices": {
|
|
"key": "unifi_devices",
|
|
"label": "UniFi — Devices",
|
|
"description": "Network infrastructure devices with online/offline state",
|
|
"hx_url": "/plugins/unifi/widget/devices",
|
|
"detail_url": "/plugins/unifi/",
|
|
"plugin": "unifi",
|
|
"poll": True,
|
|
"params": [
|
|
{
|
|
"key": "type_filter",
|
|
"label": "Device type",
|
|
"type": "select",
|
|
"default": "all",
|
|
"options": [
|
|
{"value": "all", "label": "All devices"},
|
|
{"value": "wireless", "label": "Wireless APs"},
|
|
{"value": "wired", "label": "Wired only"},
|
|
{"value": "offline", "label": "Offline devices"},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"docker_containers": {
|
|
"key": "docker_containers",
|
|
"label": "Docker — Containers",
|
|
"description": "Container status overview — running/stopped counts and per-container CPU",
|
|
"hx_url": "/plugins/docker/widget",
|
|
"detail_url": "/plugins/docker/",
|
|
"plugin": "docker",
|
|
"poll": True,
|
|
"params": [
|
|
{
|
|
"key": "show_stopped",
|
|
"label": "Show stopped",
|
|
"type": "select",
|
|
"default": "no",
|
|
"options": [
|
|
{"value": "no", "label": "Running only"},
|
|
{"value": "yes", "label": "All containers"},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"http_monitors": {
|
|
"key": "http_monitors",
|
|
"label": "HTTP Monitors",
|
|
"description": "Synthetic endpoint checks — up/down status and response times",
|
|
"hx_url": "/plugins/http/widget",
|
|
"detail_url": "/plugins/http/",
|
|
"plugin": "http",
|
|
"poll": True,
|
|
"params": [
|
|
{
|
|
"key": "show_down_only",
|
|
"label": "Display filter",
|
|
"type": "select",
|
|
"default": "no",
|
|
"options": [
|
|
{"value": "no", "label": "All monitors"},
|
|
{"value": "yes", "label": "Failing only"},
|
|
],
|
|
},
|
|
{
|
|
"key": "limit",
|
|
"label": "Max shown",
|
|
"type": "select",
|
|
"default": 10,
|
|
"options": [
|
|
{"value": 5, "label": "5 monitors"},
|
|
{"value": 10, "label": "10 monitors"},
|
|
{"value": 20, "label": "20 monitors"},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"docker_resources": {
|
|
"key": "docker_resources",
|
|
"label": "Docker — Resources",
|
|
"description": "CPU and memory usage bars for running containers",
|
|
"hx_url": "/plugins/docker/widget/resources",
|
|
"detail_url": "/plugins/docker/",
|
|
"plugin": "docker",
|
|
"poll": True,
|
|
"params": [
|
|
{
|
|
"key": "limit",
|
|
"label": "Containers shown",
|
|
"type": "select",
|
|
"default": 10,
|
|
"options": [
|
|
{"value": 5, "label": "5 containers"},
|
|
{"value": 10, "label": "10 containers"},
|
|
{"value": 20, "label": "20 containers"},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
"snmp_devices": {
|
|
"key": "snmp_devices",
|
|
"label": "SNMP — Devices",
|
|
"description": "Latest OID readings for all configured SNMP devices",
|
|
"hx_url": "/plugins/snmp/widget",
|
|
"detail_url": "/plugins/snmp/",
|
|
"plugin": "snmp",
|
|
"poll": True,
|
|
"params": [],
|
|
},
|
|
"host_resources": {
|
|
"key": "host_resources",
|
|
"label": "Host Agent — Resources",
|
|
"description": "Fleet view: CPU, memory, disk, and load per monitored host",
|
|
"hx_url": "/plugins/host_agent/widget",
|
|
"detail_url": "/hosts/",
|
|
"plugin": "host_agent",
|
|
"poll": True,
|
|
"params": [],
|
|
},
|
|
"host_resource_history": {
|
|
"key": "host_resource_history",
|
|
"label": "Host Agent — History",
|
|
"description": "CPU/memory/disk history for one host",
|
|
"hx_url": "/plugins/host_agent/widget/history",
|
|
"detail_url": "/plugins/host_agent/fleet/",
|
|
"plugin": "host_agent",
|
|
"poll": True,
|
|
"params": [
|
|
{
|
|
"key": "hours",
|
|
"label": "Time range",
|
|
"type": "select",
|
|
"default": 6,
|
|
"options": [
|
|
{"value": 1, "label": "Last 1 hour"},
|
|
{"value": 6, "label": "Last 6 hours"},
|
|
{"value": 24, "label": "Last 24 hours"},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
}
|
|
|
|
|
|
# Group each widget for the picker (Core monitors / Monitoring capabilities /
|
|
# Integrations), mirroring the Settings → Plugins taxonomy. Capability plugins
|
|
# are host/monitoring facets; traefik/unifi are discrete vendor integrations.
|
|
_CORE_WIDGETS = {"ping", "dns", "status_overview", "uptime_summary", "hosts_overview"}
|
|
_INTEGRATION_PLUGINS = {"traefik", "unifi"}
|
|
GROUP_LABELS = {
|
|
"core": "Core monitors",
|
|
"capability": "Monitoring capabilities",
|
|
"integration": "Integrations",
|
|
}
|
|
GROUP_ORDER = ["core", "capability", "integration"]
|
|
|
|
|
|
def _group_for(w: dict) -> str:
|
|
if w["key"] in _CORE_WIDGETS:
|
|
return "core"
|
|
if w.get("plugin") in _INTEGRATION_PLUGINS:
|
|
return "integration"
|
|
return "capability"
|
|
|
|
|
|
for _w in WIDGET_REGISTRY.values():
|
|
_w["group"] = _group_for(_w)
|
|
|
|
|
|
def register_widget(definition: dict) -> None:
|
|
"""Register a widget at runtime (used by external plugins)."""
|
|
definition.setdefault("group", _group_for(definition))
|
|
WIDGET_REGISTRY[definition["key"]] = definition
|
|
|
|
|
|
def get_available_widgets(plugins_cfg: dict) -> list[dict]:
|
|
"""Return widgets whose plugin dependency is satisfied."""
|
|
result = []
|
|
for w in WIDGET_REGISTRY.values():
|
|
if w["plugin"] is None:
|
|
result.append(w)
|
|
elif plugins_cfg.get(w["plugin"], {}).get("enabled", False):
|
|
result.append(w)
|
|
return result
|