diff --git a/plugins/syslog/receiver.py b/plugins/syslog/receiver.py index 2584b66..7a6f96a 100644 --- a/plugins/syslog/receiver.py +++ b/plugins/syslog/receiver.py @@ -249,6 +249,15 @@ async def store_lines(session, lines: list[Received]) -> None: sources = {(s.address, s.sender): s for s in (await session.execute( select(SyslogSource).where(SyslogSource.address.in_(addresses)))).scalars()} new = [k for k in by_sender if k not in sources] + for key in [k for k in new if k[1]]: + # A row from before devices at Docker's address were told apart by + # name, last seen under this one, is this device: it keeps its state + # and name rather than a new row being offered beside it. + before = sources.get((key[0], "")) + if before is not None and before.hostname_seen == key[1]: + before.sender = key[1] + sources[key] = sources.pop((key[0], "")) + new.remove(key) if new: known = (await session.execute(select(func.count()).select_from(SyslogSource))).scalar_one() for key in new: diff --git a/tests/integration/test_syslog.py b/tests/integration/test_syslog.py index 4e3fb45..5c58a4b 100644 --- a/tests/integration/test_syslog.py +++ b/tests/integration/test_syslog.py @@ -99,6 +99,20 @@ def test_devices_through_dockers_address_are_told_apart_by_name(app, monkeypatch got = sorted((r.address, r.sender, r.lines_total, r.last_line) for r in rows) assert got == [("10.0.0.2", "AsusNAS", 2, "d"), ("10.0.0.2", "SynNAS", 2, "c"), ("10.9.0.1", "", 1, "e")] + await s.execute(delete(SyslogSource)) + await s.commit() + # A row from before the split, last seen under a device's name, is that device's. + async with app.db_sessionmaker() as s: + async with s.begin(): + s.add(SyslogSource(address="10.0.0.3", state="accepted", name="DS923", hostname_seen="DS923", + first_seen_at=now, last_seen_at=now, lines_total=3)) + async with app.db_sessionmaker() as s: + async with s.begin(): + await receiver.store_lines(s, [line("10.0.0.3", "DS923", "f"), line("10.0.0.3", "util", "g")]) + async with app.db_sessionmaker() as s: + rows = (await s.execute(select(SyslogSource))).scalars().all() + got = sorted((r.sender, r.state, r.name, r.lines_total) for r in rows) + assert got == [("DS923", "accepted", "DS923", 4), ("util", "offered", "", 1)] async with app.db_sessionmaker() as s: async with s.begin(): await s.execute(delete(SyslogSource))