get_host_senders on VDNT-Docker02 ran past 120 s: a prefix LIKE on plugin_metrics.resource_name can't use its index (the column's collation isn't C), so each read went through the whole table, and the host page's Who's sending panel repeats it on every refresh. The containers and NFS mounts under a host are now named from plugin_metric_latest, and their series read by resource IN (...) on the series index. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
65 lines
3.0 KiB
Python
65 lines
3.0 KiB
Python
"""Integration (#6115): a host's traffic put down to its containers and NFS
|
|
mounts, read from plugin_metrics in shared time buckets on Postgres."""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import os
|
|
import uuid
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
import pytest
|
|
|
|
pytestmark = pytest.mark.integration
|
|
|
|
|
|
@pytest.fixture
|
|
def app():
|
|
if not os.environ.get("STEWARD_DATABASE_URL"):
|
|
pytest.skip("needs Postgres")
|
|
from steward.app import create_app
|
|
return create_app(testing=False)
|
|
|
|
|
|
def test_the_host_s_peak_is_put_down_to_its_senders(app):
|
|
from steward.models.metrics import PluginMetric, PluginMetricLatest
|
|
from plugins.host_agent.senders import senders
|
|
|
|
now = datetime.now(timezone.utc)
|
|
host = "senders-host-" + uuid.uuid4().hex[:8]
|
|
peak, later = now - timedelta(minutes=30), now - timedelta(minutes=10)
|
|
task = "a" * 25
|
|
|
|
def m(source, resource, metric, value, at):
|
|
return PluginMetric(source_module=source, resource_name=resource, metric_name=metric,
|
|
value=value, recorded_at=at)
|
|
|
|
async def _go():
|
|
async with app.db_sessionmaker() as s:
|
|
async with s.begin():
|
|
s.add_all([
|
|
m("host_agent", host, "net_tx_bps", 1000.0, peak),
|
|
m("host_agent", host, "net_tx_bps", 100.0, later),
|
|
# one service's two tasks add up; another host's don't count
|
|
m("docker", f"{host}/web.1.{task}", "net_tx_bps", 600.0, peak),
|
|
m("docker", f"{host}/web.2.{task}", "net_tx_bps", 200.0, peak),
|
|
m("docker", f"{host}/web.1.{task}", "net_tx_bps", 50.0, later),
|
|
m("docker", f"{host}x/web.1.{task}", "net_tx_bps", 9999.0, peak),
|
|
m("host_agent", f"{host}:/nfs/data", "nfs_tx_bps", 100.0, peak),
|
|
m("host_agent", f"{host}:net:eno1", "net_tx_bps", 1000.0, peak),
|
|
])
|
|
# the latest-value table names the resources under the host
|
|
for src, res, metric in (("docker", f"{host}/web.1.{task}", "net_tx_bps"),
|
|
("docker", f"{host}/web.2.{task}", "net_tx_bps"),
|
|
("docker", f"{host}x/web.1.{task}", "net_tx_bps"),
|
|
("host_agent", f"{host}:/nfs/data", "nfs_tx_bps")):
|
|
await s.merge(PluginMetricLatest(source_module=src, resource_name=res,
|
|
metric_name=metric, value=1.0, recorded_at=peak))
|
|
async with app.db_sessionmaker() as s:
|
|
return await senders(s, host, now - timedelta(hours=1))
|
|
|
|
got = asyncio.run(_go())
|
|
assert got["host"]["peak_bps"] == 1000.0 and got["bucket_s"] == 60
|
|
assert [(r["kind"], r["name"], r["at_host_peak_bps"]) for r in got["senders"]] == [
|
|
("container", "web", 800.0), ("nfs", "/nfs/data", 100.0)]
|
|
assert got["unaccounted_at_peak_bps"] == 100.0
|