feat(syslog): a device through Docker's address is placed by its name, or a device address set on it (#6096)
The SNMP poll asks each device's sysName in the same GET and keeps it (snmp_003_sys_name). A syslog source told apart by name finds its own address from the SNMP device (reported or given name) or host of that name, so its lines reach the SNMP device's and host's pages again; the edit page gains Device address, which always wins (syslog_004). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -102,14 +102,48 @@ async def rates(db, source_ids: list[str], now: datetime | None = None) -> dict[
|
||||
return {sid: n for sid, n in rows}
|
||||
|
||||
|
||||
def sources_for(sources: list[SyslogSource], host_id: str | None, address: str | None) -> list[SyslogSource]:
|
||||
def device_address(s: SyslogSource, named: dict[str, str] | None = None) -> str:
|
||||
"""The device's own address: the one set on the source; else, for a
|
||||
device whose lines come through Docker's address (#6096), the address of
|
||||
the SNMP device or host its name is; else the address its lines come from."""
|
||||
if s.device_address:
|
||||
return s.device_address
|
||||
if s.sender and named:
|
||||
found = named.get(s.sender.lower())
|
||||
if found:
|
||||
return found
|
||||
return s.address
|
||||
|
||||
|
||||
async def addresses_by_name(db) -> dict[str, str]:
|
||||
"""{name: address} of the SNMP devices (by the name each reports, and the
|
||||
one it was given) and hosts Steward knows, names lower-cased: how a device
|
||||
behind Docker's address is placed by the name its lines carry."""
|
||||
from steward.core.plugin_manager import loaded_plugin_submodule
|
||||
from steward.models.hosts import Host
|
||||
named: dict[str, str] = {}
|
||||
for name, address in (await db.execute(select(Host.name, Host.address))).all():
|
||||
if name and address:
|
||||
named.setdefault(name.lower(), address)
|
||||
snmp = loaded_plugin_submodule("snmp", "models")
|
||||
if snmp is not None:
|
||||
rows = await db.execute(select(snmp.SnmpDevice.sys_name, snmp.SnmpDevice.name, snmp.SnmpDevice.address))
|
||||
for sys_name, name, address in rows.all():
|
||||
for n in (sys_name, name):
|
||||
if n:
|
||||
named[n.lower()] = address # a device's own name over a host's
|
||||
return named
|
||||
|
||||
|
||||
def sources_for(sources: list[SyslogSource], host_id: str | None, address: str | None,
|
||||
named: dict[str, str] | None = None) -> list[SyslogSource]:
|
||||
"""The added sources a host's or a device's page shows: those bound to
|
||||
the host, and those bound to no host whose address is the one given. A
|
||||
binding is exclusive, as for SNMP devices: a source bound elsewhere never
|
||||
matches by address."""
|
||||
the host, and those bound to no host whose device address is the one
|
||||
given. A binding is exclusive, as for SNMP devices: a source bound
|
||||
elsewhere never matches by address."""
|
||||
address = (address or "").strip().lower()
|
||||
return [s for s in sources if s.state == "accepted" and (
|
||||
s.host_id == host_id if s.host_id else bool(address) and s.address.lower() == address)]
|
||||
s.host_id == host_id if s.host_id else bool(address) and device_address(s, named).lower() == address)]
|
||||
|
||||
|
||||
# ── Lines beside the readings: an anomaly's circumstances, a chart's marks (#6002)
|
||||
@@ -135,7 +169,8 @@ async def shown_on(db, host_id: str | None, address: str | None) -> list[SyslogS
|
||||
host = await db.get(Host, host_id)
|
||||
address = host.address if host is not None else None
|
||||
accepted = list((await db.execute(select(SyslogSource).where(SyslogSource.state == "accepted"))).scalars())
|
||||
return sources_for(accepted, host_id, address)
|
||||
named = await addresses_by_name(db) if any(s.sender for s in accepted) else None
|
||||
return sources_for(accepted, host_id, address, named)
|
||||
|
||||
|
||||
async def place_for(db, source: str, resource: str) -> tuple[str | None, str | None]:
|
||||
|
||||
Reference in New Issue
Block a user