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
+21 -5
View File
@@ -20,7 +20,7 @@ def _source(name, state, level=6, line="nfsd: server not responding", host=None)
"hostname_seen": "synnas", "host": host, "first_seen_at": now - timedelta(days=1),
"last_seen_at": now - timedelta(minutes=3), "lines_total": 1204,
"severity": "err" if level == 3 else "info", "level": level, "last_line": line,
"suggested_host": "h1", "rate": 12 if state == "accepted" else None}
"suggested_host": "h1", "rate": 12 if state == "accepted" else None, "sender": ""}
async def _render(app, role, **ctx):
@@ -62,15 +62,22 @@ async def test_a_port_it_cannot_open_is_said(app):
assert "No sources yet." in page
async def test_setup_help_names_this_server_and_opens_until_a_device_sends(app):
page = await _render(app, "viewer")
async def test_setup_help_names_the_address_set_and_opens_until_a_device_sends(app):
page = await _render(app, "viewer", server="192.168.0.10")
assert '<details class="card" id="syslog-setup" open>' in page
assert "send to <span class=\"mono\">steward.lan</span>, port <span class=\"mono\">5514</span>, UDP or TCP" in page
assert "*.* @@steward.lan:5514" in page and "mode: host" in page
assert "send to <span class=\"mono\">192.168.0.10</span>, port <span class=\"mono\">5514</span>, UDP or TCP" in page
assert "*.* @@192.168.0.10:5514" in page and "ASUSTOR (ADM)" in page
assert "<strong>Ingress</strong>" in page and "mode: host" in page
added = await _render(app, "viewer", sources=[_source("SynNAS", "accepted")])
assert '<details class="card" id="syslog-setup">' in added # closed once one sends
async def test_setup_help_never_guesses_the_address(app):
page = await _render(app, "viewer", server="")
assert "*.* @@ADDRESS:5514" in page and '"Send to"' in page
assert "steward.lan" not in page
async def test_a_sender_on_dockers_own_network_is_called_out(app):
hidden = {**_source("ingress", "offered"), "address": "10.0.0.2", "docker_address": True}
page = await _render(app, "admin", offered=[hidden, {**hidden, "id": "id-2"}])
@@ -79,6 +86,15 @@ async def test_a_sender_on_dockers_own_network_is_called_out(app):
assert "own container network" not in await _render(app, "admin", offered=[_source("synnas", "offered")])
async def test_devices_through_dockers_address_read_by_their_own_names(app):
via = {**_source("SynNAS", "offered"), "address": "10.0.0.2", "docker_address": True, "sender": "SynNAS"}
page = await _render(app, "admin", offered=[via, {**via, "id": "id-2", "sender": "AsusNAS"}])
assert "through Docker's ingress, 10.0.0.2" in page and '<span class="mono">AsusNAS</span>' in page
assert "can't be told apart" not in page and "not the device's" not in page
alone = await _render(app, "admin", offered=[{**via, "sender": ""}])
assert "carry no device name" in alone
async def test_the_edit_page_has_its_trail_and_a_remove(app):
from quart import session
source = NS(id="s1", name="SynNAS", hostname_seen="synnas", address="192.168.0.7", host_id=None)