The connection's job is keeping Home Assistant running. Each poll now reads open
repairs, Supervisor health, disk, recovery mode and add-ons that should be running
over its websocket, for the page, the overview and the problems_open, disk_free_pct
and update_failed metrics. That needs a websocket client, so websockets joins the
dependencies.
A weekly slot, set on the page or through MCP and off until then, installs add-ons,
then Core, then the OS, each after its own confirmed backup, ending at the first
failure. The slot arithmetic moves from the host agent's cadence to steward.core.weekly
so both share it.
The device scrape goes: the page's Metrics section and a target's token_from from
4c9ed02 are removed, and a migration deletes targets using it, as they'd otherwise ask
with no token. A refused token still stops a scrape.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
3.4 KiB
Python
62 lines
3.4 KiB
Python
# plugins/prometheus/models.py
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
from sqlalchemy import JSON, Boolean, DateTime, Integer, String, Text
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from steward.core.ids import time_ordered_id
|
|
from steward.models.base import Base
|
|
|
|
|
|
class PrometheusTarget(Base):
|
|
"""A /metrics endpoint Steward scrapes, edited on the Prometheus page.
|
|
|
|
The bearer token is stored encrypted. keep and drop are lists of name
|
|
patterns ("fabledarchives_*") an admin chooses: Steward does the
|
|
filtering, the endpoint serves everything. The last_* columns are the
|
|
newest scrape's outcome, written by the scraper, so a page can say why a
|
|
target isn't answering without asking it. `refused` says its token was
|
|
refused, which stops the scraping until the target is saved or resumed.
|
|
"""
|
|
__tablename__ = "prometheus_targets"
|
|
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=time_ordered_id)
|
|
name: Mapped[str] = mapped_column(String(100), nullable=False, unique=True)
|
|
url: Mapped[str] = mapped_column(String(500), nullable=False)
|
|
token: Mapped[str] = mapped_column(Text, nullable=False, default="")
|
|
verify_tls: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
|
interval_seconds: Mapped[int] = mapped_column(Integer, nullable=False, default=60)
|
|
keep: Mapped[list] = mapped_column(JSON, nullable=False, default=list)
|
|
drop: Mapped[list] = mapped_column(JSON, nullable=False, default=list)
|
|
series_cap: Mapped[int] = mapped_column(Integer, nullable=False, default=1000)
|
|
enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
|
refused: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), nullable=False, default=lambda: datetime.now(timezone.utc))
|
|
last_scraped_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
last_ok_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
last_error: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
last_kept: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
last_dropped: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
last_capped: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
|
|
|
|
|
class PrometheusWidgetTemplate(Base):
|
|
"""A widget template an admin wrote or copied in (#5692), beside the
|
|
bundled ones in widgets/*.yaml. `text` is the YAML as written; `name` is
|
|
its `name:`, kept here so the library lists and orders without reading
|
|
every template. Only a template that passes the checker is saved. Its key
|
|
in a widget's settings is "custom:<id>", so renaming it keeps its widgets.
|
|
"""
|
|
__tablename__ = "prometheus_widget_templates"
|
|
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=time_ordered_id)
|
|
name: Mapped[str] = mapped_column(String(80), nullable=False, unique=True)
|
|
text: Mapped[str] = mapped_column(Text, nullable=False)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), nullable=False, default=lambda: datetime.now(timezone.utc))
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), nullable=False, default=lambda: datetime.now(timezone.utc))
|