1
0
mirror of https://github.com/home-assistant/supervisor.git synced 2025-12-24 20:35:55 +00:00
Files
supervisor/tests/host/test_logs.py
Stefan Agner 2ebb405871 Add enhanced logging REST endpoints using systemd-journal-gatewayd (#3291)
* Add enhanced logging REST endpoints using systemd-journal-gatewayd

Add /host/logs/entries and /host/logs/{identifier}/entries to expose log
entries from systemd-journald running on the host. Use
systemd-journal-gatewayd which exposes the logs to the Supervisor via
Unix socket.

Current two query string parameters are allowed: "boot" and "follow".
The first will only return logs since last boot. The second will keep
the HTTP request open and send new log entries as they get added to the
systemd-journal.

* Allow Range header

Forward the Range header to systemd-journal-gatewayd. This allows to
select only a certain amount of log data. The Range header is a standard
header to select only partial amount of data. However, the "entries="
prefix is custom for systemd-journal-gatewayd, denoting that the numbers
following represent log entries (as opposed to bytes or other metrics).

* Avoid connecting if systemd-journal-gatewayd is not available

* Use path for all options

* Add pytests

* Address pylint issues

* Boot ID offsets and slug to identifier

* Fix tests

* API refactor from feedback

* fix tests and add identifiers

* stop isort and pylint fighting

* fix tests

* Update default log identifiers

* Only modify /host/logs endpoints

* Fix bad import

* Load log caches asynchronously at startup

* Allow task to complete in fixture

* Boot IDs and identifiers loaded on demand

* Add suggested identifiers

* Fix tests around boot ids

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2022-10-13 11:40:11 -04:00

90 lines
3.0 KiB
Python

"""Test host logs control."""
from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch
import pytest
from supervisor.coresys import CoreSys
from supervisor.exceptions import HostNotSupportedError
from supervisor.host.logs import LogsControl
from tests.common import load_fixture
TEST_BOOT_IDS = [
"b2aca10d5ca54fb1b6fb35c85a0efca9",
"b1c386a144fd44db8f855d7e907256f8",
]
async def test_load(coresys: CoreSys):
"""Test load."""
assert coresys.host.logs.default_identifiers == []
await coresys.host.logs.load()
assert coresys.host.logs.boot_ids == []
assert coresys.host.logs.identifiers == []
# File is quite large so just check it loaded
for identifier in ["kernel", "os-agent", "systemd"]:
assert identifier in coresys.host.logs.default_identifiers
async def test_logs(coresys: CoreSys, journald_gateway: MagicMock):
"""Test getting logs and errors."""
assert coresys.host.logs.available is True
async with coresys.host.logs.journald_logs() as resp:
body = await resp.text()
assert (
"Oct 11 20:46:22 odroid-dev systemd[1]: Started Hostname Service." in body
)
with patch.object(
LogsControl, "available", new=PropertyMock(return_value=False)
), pytest.raises(HostNotSupportedError):
async with coresys.host.logs.journald_logs():
pass
async def test_boot_ids(coresys: CoreSys, journald_gateway: MagicMock):
"""Test getting boot ids."""
journald_gateway.return_value.__aenter__.return_value.text = AsyncMock(
return_value=load_fixture("logs_boot_ids.txt")
)
assert TEST_BOOT_IDS == await coresys.host.logs.get_boot_ids()
# Boot ID query should not be run again, mock a failure for it to ensure
journald_gateway.side_effect = TimeoutError()
assert TEST_BOOT_IDS == await coresys.host.logs.get_boot_ids()
assert "b1c386a144fd44db8f855d7e907256f8" == await coresys.host.logs.get_boot_id(0)
# -1 is previous boot. We have 2 boots so -2 is too far
assert "b2aca10d5ca54fb1b6fb35c85a0efca9" == await coresys.host.logs.get_boot_id(-1)
with pytest.raises(ValueError):
await coresys.host.logs.get_boot_id(-2)
# 1 is oldest boot and count up from there. We have 2 boots so 3 is too far
assert "b2aca10d5ca54fb1b6fb35c85a0efca9" == await coresys.host.logs.get_boot_id(1)
assert "b1c386a144fd44db8f855d7e907256f8" == await coresys.host.logs.get_boot_id(2)
with pytest.raises(ValueError):
await coresys.host.logs.get_boot_id(3)
async def test_identifiers(coresys: CoreSys, journald_gateway: MagicMock):
"""Test getting identifiers."""
journald_gateway.return_value.__aenter__.return_value.text = AsyncMock(
return_value=load_fixture("logs_identifiers.txt")
)
# Mock is large so just look for a few different types of identifiers
for identifier in [
"addon_local_ssh",
"hassio_dns",
"hassio_supervisor",
"kernel",
"os-agent",
]:
assert identifier in await coresys.host.logs.get_identifiers()