mirror of
https://github.com/hansjone/netx.git
synced 2026-10-14 14:33:19 +08:00
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:
parent
5079343b2a
commit
214bc56774
24 changed files with 3309 additions and 1357 deletions
|
|
@ -1,4 +1,4 @@
|
|||
"""Pydantic schemas for port traffic monitoring API."""
|
||||
"""Pydantic schemas for port traffic monitoring API (device-centric)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -8,17 +8,17 @@ from typing import Literal
|
|||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class PortTrafficNeRef(BaseModel):
|
||||
source: Literal["managed", "ume"]
|
||||
id: str
|
||||
ne_name: str = ""
|
||||
ne_ip: str = ""
|
||||
vendor: str = ""
|
||||
class PortTrafficIfaceIn(BaseModel):
|
||||
ifname: str
|
||||
if_description: str = ""
|
||||
bw_bps: int = 0
|
||||
|
||||
|
||||
class PortTrafficTargetIn(BaseModel):
|
||||
source: Literal["managed", "ume"]
|
||||
target_id: str
|
||||
"""Legacy-shaped input; create/device APIs prefer PortTrafficIfaceIn under one NE."""
|
||||
|
||||
source: Literal["managed", "ume"] = "managed"
|
||||
target_id: str = ""
|
||||
ne_name: str = ""
|
||||
ne_ip: str = ""
|
||||
vendor: str = ""
|
||||
|
|
@ -27,25 +27,38 @@ class PortTrafficTargetIn(BaseModel):
|
|||
bw_bps: int = 0
|
||||
|
||||
|
||||
class PortTrafficTaskCreate(BaseModel):
|
||||
title: str = Field(min_length=1, max_length=256)
|
||||
class PortTrafficDeviceCreate(BaseModel):
|
||||
source: Literal["managed", "ume"]
|
||||
ne_id: str = Field(min_length=1, max_length=128)
|
||||
ne_name: str = ""
|
||||
ne_ip: str = ""
|
||||
vendor: str = ""
|
||||
note: str = Field(default="", max_length=256)
|
||||
interval_sec: int = Field(default=60, ge=15, le=3600)
|
||||
retention_days: int = Field(default=7, ge=1, le=90)
|
||||
concurrency: int = Field(default=5, ge=1, le=20)
|
||||
targets: list[PortTrafficTargetIn] = Field(default_factory=list)
|
||||
concurrency: int = Field(default=1, ge=1, le=5)
|
||||
interfaces: list[PortTrafficIfaceIn] = Field(default_factory=list)
|
||||
start_now: bool = False
|
||||
|
||||
|
||||
class PortTrafficTaskUpdate(BaseModel):
|
||||
title: str | None = Field(default=None, min_length=1, max_length=256)
|
||||
class PortTrafficDeviceUpdate(BaseModel):
|
||||
note: str | None = Field(default=None, max_length=256)
|
||||
interval_sec: int | None = Field(default=None, ge=15, le=3600)
|
||||
retention_days: int | None = Field(default=None, ge=1, le=90)
|
||||
concurrency: int | None = Field(default=None, ge=1, le=20)
|
||||
concurrency: int | None = Field(default=None, ge=1, le=5)
|
||||
ne_name: str | None = None
|
||||
ne_ip: str | None = None
|
||||
vendor: str | None = None
|
||||
|
||||
|
||||
class PortTrafficTaskOut(BaseModel):
|
||||
class PortTrafficDeviceOut(BaseModel):
|
||||
id: str
|
||||
title: str
|
||||
source: str
|
||||
ne_id: str
|
||||
ne_name: str
|
||||
ne_ip: str
|
||||
vendor: str
|
||||
note: str = ""
|
||||
status: str
|
||||
interval_sec: int
|
||||
retention_days: int
|
||||
|
|
@ -62,7 +75,8 @@ class PortTrafficTaskOut(BaseModel):
|
|||
|
||||
class PortTrafficTargetOut(BaseModel):
|
||||
id: str
|
||||
task_id: str
|
||||
device_id: str
|
||||
task_id: str = "" # alias of device_id for older clients
|
||||
series_id: str = ""
|
||||
source: str
|
||||
target_id: str
|
||||
|
|
@ -80,7 +94,8 @@ class PortTrafficTargetOut(BaseModel):
|
|||
|
||||
class PortTrafficSeriesOut(BaseModel):
|
||||
id: str
|
||||
task_id: str
|
||||
device_id: str
|
||||
task_id: str = ""
|
||||
title: str
|
||||
status: str
|
||||
active_target: PortTrafficTargetOut | None = None
|
||||
|
|
@ -88,18 +103,13 @@ class PortTrafficSeriesOut(BaseModel):
|
|||
created_at: datetime | None = None
|
||||
|
||||
|
||||
class PortTrafficTargetsPut(BaseModel):
|
||||
targets: list[PortTrafficTargetIn]
|
||||
class PortTrafficInterfacesPut(BaseModel):
|
||||
"""Replace active interface set for a device (keeps samples for retired/removed)."""
|
||||
|
||||
interfaces: list[PortTrafficIfaceIn]
|
||||
|
||||
|
||||
class PortTrafficReplacePortRequest(BaseModel):
|
||||
"""Replace the active interface under a logical series (keeps history)."""
|
||||
|
||||
source: Literal["managed", "ume"]
|
||||
target_id: str
|
||||
ne_name: str = ""
|
||||
ne_ip: str = ""
|
||||
vendor: str = ""
|
||||
ifname: str
|
||||
if_description: str = ""
|
||||
bw_bps: int = 0
|
||||
|
|
@ -141,7 +151,7 @@ class PortTrafficSamplePoint(BaseModel):
|
|||
out_util_pct: float
|
||||
bw_bps: int
|
||||
rate_period_sec: int = 0
|
||||
ts_raw: datetime | None = None # baseline original time when aligned
|
||||
ts_raw: datetime | None = None
|
||||
|
||||
|
||||
class PortTrafficSamplesOut(BaseModel):
|
||||
|
|
@ -166,8 +176,26 @@ class PortTrafficCompareOut(BaseModel):
|
|||
|
||||
|
||||
class PortTrafficDashboardOut(BaseModel):
|
||||
task_count: int = 0
|
||||
running_task_count: int = 0
|
||||
device_count: int = 0
|
||||
running_device_count: int = 0
|
||||
active_target_count: int = 0
|
||||
sample_count_24h: int = 0
|
||||
last_sample_at: datetime | None = None
|
||||
# Back-compat aliases for older UI
|
||||
task_count: int = 0
|
||||
running_task_count: int = 0
|
||||
|
||||
|
||||
class PortTrafficEventOut(BaseModel):
|
||||
id: str
|
||||
device_id: str
|
||||
target_row_id: str = ""
|
||||
ifname: str = ""
|
||||
level: str = "error"
|
||||
message: str = ""
|
||||
created_at: datetime | None = None
|
||||
|
||||
|
||||
class PortTrafficEventsOut(BaseModel):
|
||||
items: list[PortTrafficEventOut] = Field(default_factory=list)
|
||||
total: int = 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue