feat(syslog): devices through Swarm's ingress told apart by name; the send-to address is a setting (#6096)
CI / lint (push) Successful in 2s
CI / unit (push) Failing after 1m30s
CI / integration (push) Successful in 1m38s
CI / publish (push) Skipped

Through ingress every device arrives from Docker's own address, so a
source there is keyed by the hostname its lines carry (new column
sender, unique with address). The set-up help no longer guesses the
address from the web hostname: it shows the plugin's send_to setting,
covers ingress and host mode, ASUSTOR ADM, and both syslog formats.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-19 12:05:28 -04:00
co-authored by Claude Opus 5.5
parent d2926b6663
commit 0a647bbb82
11 changed files with 193 additions and 53 deletions
+9 -2
View File
@@ -3,7 +3,8 @@ from __future__ import annotations
from datetime import datetime, timezone
from sqlalchemy import JSON, BigInteger, Boolean, DateTime, ForeignKey, Index, SmallInteger, String, Text
from sqlalchemy import (JSON, BigInteger, Boolean, DateTime, ForeignKey, Index, SmallInteger, String, Text,
UniqueConstraint)
from sqlalchemy.orm import Mapped, mapped_column
from steward.core.ids import time_ordered_id
@@ -13,6 +14,10 @@ from steward.models.base import Base
class SyslogSource(Base):
"""A device that sends syslog to Steward, one row per sending address.
Through a port Swarm publishes on its ingress (the default), every device
arrives from one address of Docker's own; there a row is a device by the
name its lines carry, `sender` (#6096). Everywhere else `sender` is "".
A sender Steward hasn't been told about is a row too, `state="offered"`:
its lines are counted and its newest kept as a sample, so the page can
offer it ("1,204 lines from 192.168.0.7: add as a source?"), but none is
@@ -20,9 +25,11 @@ class SyslogSource(Base):
on; `"ignored"` stops the offer and keeps counting.
"""
__tablename__ = "syslog_sources"
__table_args__ = (UniqueConstraint("address", "sender", name="uq_syslog_sources_address_sender"),)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=time_ordered_id)
address: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
address: Mapped[str] = mapped_column(String(64), nullable=False)
sender: Mapped[str] = mapped_column(String(255), nullable=False, default="")
state: Mapped[str] = mapped_column(String(10), nullable=False, default="offered")
name: Mapped[str] = mapped_column(String(100), nullable=False, default="")
host_id: Mapped[str | None] = mapped_column(