"""The Syslog page (#5999): whether the listener is open or why not, the
sources kept, and the senders offered to an admin to add or ignore."""
from datetime import datetime, timedelta, timezone
from types import SimpleNamespace as NS
from tests._templates import render_plugin_template
from tests.test_style_tokens import hex_colours
def _listener(**over):
base = {"port": 5514, "protocols": ["UDP", "TCP"], "listening": True, "error": None,
"bound_at": datetime.now(timezone.utc) - timedelta(hours=2), "received": 1204,
"dropped": 0, "unoffered": 0, "connections": 1, "senders_full": False, "max_senders": 200}
return {**base, **over}
def _source(name, state, level=6, line="nfsd: server not responding", host=None):
now = datetime.now(timezone.utc)
return {"id": f"id-{name}", "address": "192.168.0.7", "state": state, "name": name,
"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, "sender": ""}
async def _render(app, role, **ctx):
from quart import session
ctx = {"listener": _listener(), "sources": [], "offered": [], "ignored": [],
"hosts": [NS(id="h1", name="SynNAS", address="192.168.0.7")],
"server": "steward.lan", "now": datetime.now(timezone.utc), **ctx}
async with app.test_request_context("/plugins/syslog/"):
session["user_role"] = role
page = await render_plugin_template(app, "syslog", "syslog/index.html", **ctx)
return " ".join(page.split())
async def test_an_offered_sender_can_be_added_or_ignored_by_an_admin(app):
page = await _render(app, "admin", offered=[_source("synnas", "offered", level=3)])
assert "Sending, not added" in page and "calls itself synnas" in page
assert 'action="/plugins/syslog/sources/id-synnas/add"' in page
assert 'action="/plugins/syslog/sources/id-synnas/ignore"' in page
assert '' in page # the host at its address
assert 'class="tone-crit">err nfsd: server not responding' in page
assert "UDP and TCP port 5514" in page and "1,204 lines received" in page
main = page[page.index("' in page
assert "send to 192.168.0.10, port 5514, UDP or TCP" in page
assert "*.* @@192.168.0.10:5514" in page and "ASUSTOR (ADM)" in page
assert "Ingress" in page and "mode: host" in page
added = await _render(app, "viewer", sources=[_source("SynNAS", "accepted")])
assert '' 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"}])
assert "Lines from 10.0.0.2 come through Docker's own address and carry no device name" in page
assert page.count("Docker's address, not the device's") == 2
assert "carry no device name" 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 'AsusNAS' 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)
async with app.test_request_context("/plugins/syslog/sources/s1/edit"):
session["user_role"] = "admin"
page = await render_plugin_template(app, "syslog", "syslog/edit.html", source=source,
hosts=[], label="SynNAS")
page = " ".join(page.split())
assert 'href="/plugins/syslog/">Syslog' in page
assert 'action="/plugins/syslog/sources/s1/delete"' in page and "data-confirm=" in page
def _line(i, level=6, app="kernel", message="nfsd: server not responding", minutes=1):
now = datetime.now(timezone.utc)
return {"id": f"l{i}", "received_at": now - timedelta(minutes=minutes), "sent_at": None,
"level": level, "severity": {3: "err", 4: "warning"}.get(level, "info"),
"facility": "kern", "hostname": "synnas", "app": app, "message": message,
"group": "Today", "opens_group": i == 0}
async def test_a_source_page_filters_its_lines_and_scrolls_on(app):
from quart import session
from plugins.syslog.service import SEVERITY_CHOICES, LineFilter
src = {**_source("SynNAS", "accepted"), "rate": 42}
async with app.test_request_context("/plugins/syslog/sources/id-SynNAS"):
session["user_role"] = "viewer"
page = await render_plugin_template(
app, "syslog", "syslog/source.html", source=src, f=LineFilter(severity=3), filtered=True,
lines=[_line(0, level=3), _line(1, app="", message="a tag")],
more_url="/plugins/syslog/sources/id-SynNAS?severity=3&before=x", severities=SEVERITY_CHOICES,
apps=["kernel", "nfsd"], ranges=["1h", "24h"])
page = " ".join(page.split())
assert 'href="/plugins/syslog/">Syslog' in page # its trail
assert "42 lines in the last hour" in page
assert '' in page
assert '
Today
' in page
assert 'err' in page and "a <b> tag" in page
assert 'hx-get="/plugins/syslog/sources/id-SynNAS?severity=3&before=x"' in page
assert "/edit" not in page # a viewer
main = page[page.index("Source" not in one and 'warning' in one
assert "
Source
" in two and 'href="/plugins/syslog/sources/s2">router' in two
assert "No lines in the last day." in none