Files
FabledSteward/plugins/prometheus/__init__.py
T
bvandeusenandClaude Opus 5.5 eee8469a17
CI / lint (push) Successful in 2s
CI / unit (push) Successful in 1m26s
CI / integration (push) Successful in 1m33s
CI / publish (push) Successful in 19s
feat(mcp): SNMP devices and Prometheus targets: their state, readings and series (#5840)
The snmp plugin registers list_snmp_devices and get_snmp_device; the
prometheus plugin registers list_prometheus_targets and
get_prometheus_target. Both are viewer-level, read what the poller and
scraper stored, and exist only while their plugin is enabled.
get_overview names a device or target whose last poll or scrape failed;
stale, pending and off are left out.

A device's community and v3 keys and a target's token are never
returned, and a password in a target URL is masked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 13:31:46 -04:00

81 lines
2.7 KiB
Python

# plugins/prometheus/__init__.py
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from quart import Quart
_app: "Quart | None" = None
def setup(app: "Quart") -> None:
global _app
_app = app
from .models import PrometheusTarget, PrometheusWidgetTemplate # noqa: F401 — registers the tables
register_widgets()
def register_widgets() -> None:
"""One dashboard widget for every target and template (System 181's
ruling). Its target and template lists are read when the widget picker
opens, so a new target or template appears without a restart."""
from steward.core.widgets import register_widget
from . import library
register_widget({
"key": "prometheus",
"label": "Prometheus",
"description": "One scrape target drawn by a widget template: Fabled Archives, or one you copy in",
"hx_url": "/plugins/prometheus/widget",
"detail_url": "/plugins/prometheus/targets/{target}",
"plugin": "prometheus",
"poll": True,
"default_title": library.default_title,
"params": [
{"key": "target", "label": "Target", "type": "select", "default": "",
"options_from": library.target_options,
"empty": "No targets yet: add one under Prometheus first."},
{"key": "template", "label": "Template", "type": "select", "default": "",
"options_from": library.template_options,
"empty": "No templates yet."},
],
})
def get_scheduled_tasks() -> list:
from .scheduler import make_scrape_task
return [make_scrape_task(_app)]
def get_blueprint():
from .routes import prometheus_bp
return prometheus_bp
def get_nav() -> list[dict]:
# Surfaces in the sidebar's Infrastructure group (scraped endpoints).
return [{"label": "Prometheus", "href": "/plugins/prometheus/"}]
def get_alert_metrics() -> dict[str, list[str]]:
"""The metric names are whatever each endpoint exposes, so the list is
empty here and the rule form offers the names recorded so far."""
return {"prometheus": []}
def get_audit_entities() -> dict:
"""Audit rows about a target or a widget template name it and link to its page."""
from .models import PrometheusTarget, PrometheusWidgetTemplate
return {"prometheus_target": (PrometheusTarget, "/plugins/prometheus/targets/{}"),
"prometheus_template": (PrometheusWidgetTemplate, "/plugins/prometheus/templates/custom:{}")}
def register_mcp_tools(register) -> None:
from .mcp_tools import register_tools
register_tools(register)
async def mcp_overview(db) -> list[dict]:
from .mcp_tools import mcp_overview as findings
return await findings(db)