"""The host page says how the host is in one header, and grades every reading by Settings → Thresholds, nowhere else (#5122).""" import re from datetime import datetime, timedelta, timezone from types import SimpleNamespace import pytest from steward.core.formatting import worst_reading from steward.core.settings import to_thresholds_cfg from tests._templates import render_plugin_template from tests.test_style_tokens import hex_colours TH = to_thresholds_cfg({}) # cpu/mem/disk warn 80 crit 90; load warn 80 crit 100 @pytest.mark.parametrize("readings, worst", [ ([("CPU", 40, "cpu"), ("Memory", 50, "mem")], None), ([("CPU", None, "cpu")], None), # no reading is not a problem ([("CPU", 85, "cpu"), ("Memory", 95, "mem")], ("Memory", 95, "crit")), # crit beats warn ([("CPU", 92, "cpu"), ("Memory", 95, "mem")], ("CPU", 92, "crit")), # first of equals ([("Disk /", 81, "disk")], ("Disk /", 81, "warn")), ]) def test_worst_reading(readings, worst): assert worst_reading(readings, TH) == worst async def _vitals(app, *, cpu=40.0, mem=50.0, stale=False, seen_min=1): readings = (("CPU", cpu, "cpu"), ("Memory", mem, "mem"), ("Disk /", 30.0, "disk"), ("Load per core", 20, "load")) async with app.test_request_context("/plugins/host_agent/vitals/h1"): page = await render_plugin_template( app, "host_agent", "_host_vitals.html", reporting=True, stale=stale, reg=SimpleNamespace(last_seen_at=datetime.now(timezone.utc) - timedelta(minutes=seen_min), agent_version="1.9.0"), cpu=cpu, mem=mem, disk_root=30.0, load_per_core=20, sparks={"cpu": "", "mem": "", "disk": "", "load": ""}, psi={"cpu": None, "mem": None, "io": None}, worst=worst_reading(readings, TH)) return re.sub(r">\s+<", "><", " ".join(page.split())) async def test_a_host_over_a_threshold_says_which_reading(app): page = await _vitals(app, cpu=40.0, mem=93.0) assert 'Live' in page assert 'Memory 93% is past its critical threshold.' in page assert 'class="stat-tile stat-crit">Memory' in page assert 'class="stat-tile stat-ok">CPU' in page assert "seen Stale' in page assert "No report since 20m ago; the readings below are from then." in page assert "past its" not in page # an old reading is not news async def test_the_metrics_page_grades_by_the_same_cutoffs(app): # 75 % was amber under the old hard-coded 70/90; Settings → Thresholds says warn at 80. async with app.test_request_context("/plugins/host_agent/h1/metrics"): page = await render_plugin_template( app, "host_agent", "_host_metrics.html", host=SimpleNamespace(address="10.0.0.2"), stale=False, reg=SimpleNamespace(distro=None, kernel=None, arch=None, agent_version=None, last_seen_at=datetime.now(timezone.utc)), hostlvl={"cpu_pct": 75.0, "mem_used_pct": 95.0, "temp_c_max": 72.0}, cores=[(0, 85.0)], nets={}, disks_io={}, temps={"cpu": 90.0}, mounts={"/": {"disk_used_pct": 50.0, "disk_used_bytes": 1, "disk_total_bytes": 2}}) assert 'class="stat-val tone-ok" style="font-size:1.6rem;">75%' in page assert 'class="stat-val tone-crit" style="font-size:1.6rem;">95%' in page assert 'class="meter-fill meter-ok" style="width:75.0%;"' in page assert 'class="meter-fill meter-warn" style="width:85.0%;"' in page # the core assert 'class="stat-val tone-warn" style="font-size:1.6rem;">72°C' in page assert '90°C' in page assert not hex_colours(page), hex_colours(page) async def test_sensors_not_counted_are_listed_once_per_reason(app): """Util01's five open inputs read as one list with its reason, not five repeats of it; a sensor judged against the CPU carries its own (#5783).""" held = "reads a value no wired sensor gives" falls = "falls as the CPU warms (r -0.81 over 168 h)" async with app.test_request_context("/plugins/host_agent/h1/metrics"): page = await render_plugin_template( app, "host_agent", "_host_metrics.html", host=SimpleNamespace(address="10.0.0.2"), stale=False, reg=SimpleNamespace(distro=None, kernel=None, arch=None, agent_version=None, last_seen_at=datetime.now(timezone.utc)), hostlvl={"temp_c_max": 52.0}, cores=[], nets={}, disks_io={}, temps={"Package id 0": 52.0}, unwired_temps={"SYSTIN": (115.0, held), "AUXTIN0": (110.0, held), "temp3": (62.0, falls)}, mounts={}) page = " ".join(page.split()) assert (f"Not counted, likely nothing wired to them: temp3 62°C ({falls}); " f"SYSTIN 115°C, AUXTIN0 110°C ({held}).") in page async def test_an_nfs_mount_says_what_a_request_took_and_a_local_one_says_nothing(app): """#5996: the delay under each NFS mount's bar, graded at 100 and 1000 ms.""" async with app.test_request_context("/plugins/host_agent/h1/metrics"): page = await render_plugin_template( app, "host_agent", "_host_metrics.html", host=SimpleNamespace(address="10.0.0.2"), stale=False, reg=SimpleNamespace(distro=None, kernel=None, arch=None, agent_version=None, last_seen_at=datetime.now(timezone.utc)), hostlvl={"cpu_pct": 10.0}, cores=[], nets={}, disks_io={}, temps={}, mounts={"/nfs/data": {"disk_used_pct": 40.0, "nfs_ops_per_sec": 4.0, "nfs_exe_ms": 250.0, "nfs_retrans": 3.0}, "/nfs/media": {"disk_used_pct": 30.0, "nfs_ops_per_sec": 0.0}, "/": {"disk_used_pct": 20.0}}) page = re.sub(r">\s+<", "><", " ".join(page.split())) assert '250.0 ms a request · 4/s · 3 sent again' in page assert "NFS idle" in page and page.count("NFS ") == 2 assert "timed out" not in page assert not hex_colours(page), hex_colours(page)