Move port traffic to device-centric monitoring with a dedicated wall and collect logs.

Split the ops wall to its own route with fullscreen charts, persist collect errors as device events, and polish wall legend layout.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-08-01 15:45:20 +08:00
parent 5079343b2a
commit 214bc56774
24 changed files with 3309 additions and 1357 deletions

View file

@ -11,7 +11,7 @@ from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool
from netx_api.db import Base
from netx_api.models import PortTrafficSample, PortTrafficSeries, PortTrafficTarget, PortTrafficTask
from netx_api.models import PortTrafficDevice, PortTrafficSample, PortTrafficSeries, PortTrafficTarget
from netx_api.port_traffic_service import baseline_offset_hours, compare_targets
@ -34,16 +34,32 @@ class CompareTargetsTests(unittest.TestCase):
Base.metadata.create_all(bind=engine)
self.Session = sessionmaker(bind=engine)
self.db = self.Session()
self.task_id = uuid4().hex
self.device_id = uuid4().hex
self.device2_id = uuid4().hex
self.series_id = uuid4().hex
self.target_id = uuid4().hex
self.mapped_id = uuid4().hex
now = datetime(2026, 7, 31, 12, 0, 0)
self.now = now
self.db.add(
PortTrafficTask(
id=self.task_id,
title="t1",
PortTrafficDevice(
id=self.device_id,
source="managed",
ne_id="ne1",
ne_name="R1",
vendor="Huawei",
status="running",
created_at=now,
updated_at=now,
)
)
self.db.add(
PortTrafficDevice(
id=self.device2_id,
source="managed",
ne_id="ne2",
ne_name="R2",
vendor="ZTE",
status="running",
created_at=now,
updated_at=now,
@ -52,7 +68,7 @@ class CompareTargetsTests(unittest.TestCase):
self.db.add(
PortTrafficSeries(
id=self.series_id,
task_id=self.task_id,
device_id=self.device_id,
title="uplink1",
status="active",
created_at=now,
@ -61,7 +77,7 @@ class CompareTargetsTests(unittest.TestCase):
self.db.add(
PortTrafficTarget(
id=self.target_id,
task_id=self.task_id,
device_id=self.device_id,
series_id=self.series_id,
source="managed",
target_id="ne1",
@ -75,7 +91,7 @@ class CompareTargetsTests(unittest.TestCase):
self.db.add(
PortTrafficTarget(
id=self.mapped_id,
task_id=self.task_id,
device_id=self.device2_id,
series_id="",
source="managed",
target_id="ne2",
@ -86,7 +102,6 @@ class CompareTargetsTests(unittest.TestCase):
created_at=now,
)
)
# current window points (last 2h) on primary interface
for i in range(3):
self.db.add(
PortTrafficSample(
@ -99,7 +114,6 @@ class CompareTargetsTests(unittest.TestCase):
raw_ok=True,
)
)
# baseline day-1 points on same port
for i in range(3):
self.db.add(
PortTrafficSample(
@ -112,7 +126,6 @@ class CompareTargetsTests(unittest.TestCase):
raw_ok=True,
)
)
# mapped port same-window points
for i in range(2):
self.db.add(
PortTrafficSample(
@ -175,7 +188,6 @@ class CompareTargetsTests(unittest.TestCase):
self.assertIsNone(out.baseline[0].ts_raw)
def test_mapped_port_with_day_offset(self):
# day-ago samples on mapped port
for i in range(2):
self.db.add(
PortTrafficSample(

View file

@ -15,6 +15,7 @@ from netx_api.port_traffic_parsers import (
parse_interface_detail,
parse_zte_interface_brief,
parse_zte_interface_detail,
resolve_util_pct,
)
SAMPLE_DETAIL = """\
@ -280,5 +281,20 @@ class CommandsTests(unittest.TestCase):
)
class ResolveUtilTests(unittest.TestCase):
def test_keeps_vendor_util(self):
self.assertEqual(resolve_util_pct(0.01, 0.0, 1_000_000_000), 0.01)
def test_derives_when_vendor_zero(self):
# 98 Kbit/s on 1 Gbit/s ≈ 0.0098%
self.assertAlmostEqual(resolve_util_pct(0.0, 98_000.0, 1_000_000_000), 0.0098)
def test_zero_when_no_traffic(self):
self.assertEqual(resolve_util_pct(0.0, 0.0, 1_000_000_000), 0.0)
def test_zero_when_no_bw(self):
self.assertEqual(resolve_util_pct(0.0, 98_000.0, 0), 0.0)
if __name__ == "__main__":
unittest.main()