mirror of
https://github.com/hansjone/netx.git
synced 2026-10-08 23:33:21 +08:00
Keep the public facade stable while moving device CRUD and sample/compare logic into focused modules, and use naive UTC via timeutil plus FastAPI lifespan. Co-authored-by: Cursor <cursoragent@cursor.com>
159 lines
4.6 KiB
Python
159 lines
4.6 KiB
Python
"""Shared helpers for port traffic device/sample services."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any
|
|
from uuid import uuid4
|
|
|
|
from fastapi import HTTPException
|
|
from sqlalchemy.orm import Session
|
|
|
|
from .models import (
|
|
PortTrafficDevice,
|
|
PortTrafficSeries,
|
|
PortTrafficTarget,
|
|
)
|
|
from .port_traffic_commands import commands_for_vendor
|
|
from .port_traffic_migrate import default_series_title, unique_series_title
|
|
from .port_traffic_schemas import (
|
|
PortTrafficDeviceOut,
|
|
PortTrafficIfaceIn,
|
|
PortTrafficTargetOut,
|
|
)
|
|
from .timeutil import utcnow_naive
|
|
|
|
|
|
def _utcnow() -> datetime:
|
|
return utcnow_naive()
|
|
|
|
|
|
def _target_out(row: PortTrafficTarget) -> PortTrafficTargetOut:
|
|
did = str(row.device_id or "")
|
|
return PortTrafficTargetOut(
|
|
id=str(row.id),
|
|
device_id=did,
|
|
task_id=did,
|
|
series_id=str(row.series_id or ""),
|
|
source=str(row.source or ""),
|
|
target_id=str(row.target_id or ""),
|
|
ne_name=str(row.ne_name or ""),
|
|
ne_ip=str(row.ne_ip or ""),
|
|
vendor=str(row.vendor or ""),
|
|
ifname=str(row.ifname or ""),
|
|
if_description=str(row.if_description or ""),
|
|
bw_bps=int(row.bw_bps or 0),
|
|
status=str(row.status or ""),
|
|
last_error=str(row.last_error or ""),
|
|
last_sample_at=row.last_sample_at,
|
|
created_at=row.created_at,
|
|
)
|
|
|
|
|
|
def _device_out(db: Session, device: PortTrafficDevice) -> PortTrafficDeviceOut:
|
|
did = str(device.id)
|
|
total = db.query(PortTrafficTarget).filter(PortTrafficTarget.device_id == did).count()
|
|
active = (
|
|
db.query(PortTrafficTarget)
|
|
.filter(PortTrafficTarget.device_id == did, PortTrafficTarget.status == "active")
|
|
.count()
|
|
)
|
|
return PortTrafficDeviceOut(
|
|
id=did,
|
|
source=str(device.source or ""),
|
|
ne_id=str(device.ne_id or ""),
|
|
ne_name=str(device.ne_name or ""),
|
|
ne_ip=str(device.ne_ip or ""),
|
|
vendor=str(device.vendor or ""),
|
|
note=str(device.note or ""),
|
|
status=str(device.status or ""),
|
|
interval_sec=int(device.interval_sec or 60),
|
|
retention_days=int(device.retention_days or 7),
|
|
concurrency=int(device.concurrency or 1),
|
|
collect_running=bool(device.collect_running),
|
|
target_count=int(total),
|
|
active_target_count=int(active),
|
|
last_collect_started_at=device.last_collect_started_at,
|
|
last_collect_ended_at=device.last_collect_ended_at,
|
|
last_error=str(device.last_error or ""),
|
|
created_at=device.created_at,
|
|
updated_at=device.updated_at,
|
|
)
|
|
|
|
|
|
def _assert_vendor(vendor: str, label: str) -> None:
|
|
cmds = commands_for_vendor(vendor or "", "")
|
|
if cmds is None:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"vendor_not_supported_for_port_traffic: {vendor or 'unknown'} ({label})",
|
|
)
|
|
|
|
|
|
def _assert_ifaces_free(
|
|
db: Session,
|
|
*,
|
|
source: str,
|
|
ne_id: str,
|
|
ifnames: list[str],
|
|
exclude_device_id: str | None = None,
|
|
) -> None:
|
|
names = [str(x).strip() for x in ifnames if str(x).strip()]
|
|
if not names:
|
|
return
|
|
q = db.query(PortTrafficTarget).filter(
|
|
PortTrafficTarget.source == source,
|
|
PortTrafficTarget.target_id == ne_id,
|
|
PortTrafficTarget.ifname.in_(names),
|
|
PortTrafficTarget.status == "active",
|
|
)
|
|
if exclude_device_id:
|
|
q = q.filter(PortTrafficTarget.device_id != exclude_device_id)
|
|
hit = q.first()
|
|
if hit:
|
|
raise HTTPException(
|
|
status_code=409,
|
|
detail=f"interface_already_monitored: {hit.ifname} on device {hit.device_id}",
|
|
)
|
|
|
|
|
|
def _create_iface(
|
|
db: Session,
|
|
*,
|
|
device: PortTrafficDevice,
|
|
iface: PortTrafficIfaceIn,
|
|
now: datetime,
|
|
) -> PortTrafficTarget:
|
|
ifname = iface.ifname.strip()
|
|
title = unique_series_title(
|
|
db, str(device.id), default_series_title(device.ne_name or "", ifname)
|
|
)
|
|
sid = uuid4().hex
|
|
db.add(
|
|
PortTrafficSeries(
|
|
id=sid,
|
|
device_id=str(device.id),
|
|
title=title,
|
|
status="active",
|
|
created_at=now,
|
|
)
|
|
)
|
|
row = PortTrafficTarget(
|
|
id=uuid4().hex,
|
|
device_id=str(device.id),
|
|
series_id=sid,
|
|
source=str(device.source),
|
|
target_id=str(device.ne_id),
|
|
ne_name=str(device.ne_name or ""),
|
|
ne_ip=str(device.ne_ip or ""),
|
|
vendor=str(device.vendor or ""),
|
|
ifname=ifname,
|
|
if_description=iface.if_description or "",
|
|
bw_bps=int(iface.bw_bps or 0),
|
|
status="active",
|
|
created_at=now,
|
|
)
|
|
db.add(row)
|
|
return row
|
|
|
|
|