fix: expand bgp pipeline and stabilize backend tests

This commit is contained in:
linkong
2026-03-30 16:13:36 +08:00
parent 2015ab79bd
commit 945786cee5
22 changed files with 1787 additions and 228 deletions

View File

@@ -6,6 +6,8 @@ from app.models.datasource import DataSource
from app.models.datasource_config import DataSourceConfig
from app.models.alert import Alert, AlertSeverity, AlertStatus
from app.models.bgp_anomaly import BGPAnomaly
from app.models.bgp_incident import BGPIncident
from app.models.bgp_observation import BGPObservation
from app.models.system_setting import SystemSetting
__all__ = [
@@ -20,4 +22,6 @@ __all__ = [
"AlertSeverity",
"AlertStatus",
"BGPAnomaly",
"BGPIncident",
"BGPObservation",
]

View File

@@ -0,0 +1,64 @@
"""BGP incident model for aggregated routing events."""
from datetime import datetime
from sqlalchemy import Column, DateTime, Float, ForeignKey, Index, Integer, JSON, String, Text
from app.core.time import to_iso8601_utc
from app.db.session import Base
class BGPIncident(Base):
__tablename__ = "bgp_incidents"
id = Column(Integer, primary_key=True, index=True)
snapshot_id = Column(Integer, ForeignKey("data_snapshots.id"), nullable=True, index=True)
task_id = Column(Integer, ForeignKey("collection_tasks.id"), nullable=True, index=True)
source = Column(String(100), nullable=False, index=True)
incident_key = Column(String(255), nullable=False, index=True)
incident_type = Column(String(50), nullable=False, index=True)
title = Column(String(255), nullable=False)
summary = Column(Text, nullable=False)
severity = Column(String(20), nullable=False, index=True)
status = Column(String(20), nullable=False, default="active", index=True)
confidence = Column(Float, nullable=False, default=0.5)
started_at = Column(DateTime(timezone=True), nullable=False, default=datetime.utcnow, index=True)
ended_at = Column(DateTime(timezone=True), nullable=True)
affected_prefixes = Column(JSON, default=list)
affected_asns = Column(JSON, default=list)
affected_collectors = Column(JSON, default=list)
affected_regions = Column(JSON, default=list)
related_cables = Column(JSON, default=list)
related_ixps = Column(JSON, default=list)
evidence_refs = Column(JSON, default=list)
created_at = Column(DateTime(timezone=True), nullable=False, default=datetime.utcnow, index=True)
__table_args__ = (
Index("idx_bgp_incidents_source_created", "source", "created_at"),
Index("idx_bgp_incidents_type_status", "incident_type", "status"),
)
def to_dict(self) -> dict:
return {
"id": self.id,
"snapshot_id": self.snapshot_id,
"task_id": self.task_id,
"source": self.source,
"incident_key": self.incident_key,
"incident_type": self.incident_type,
"title": self.title,
"summary": self.summary,
"severity": self.severity,
"status": self.status,
"confidence": self.confidence,
"started_at": to_iso8601_utc(self.started_at),
"ended_at": to_iso8601_utc(self.ended_at),
"affected_prefixes": self.affected_prefixes or [],
"affected_asns": self.affected_asns or [],
"affected_collectors": self.affected_collectors or [],
"affected_regions": self.affected_regions or [],
"related_cables": self.related_cables or [],
"related_ixps": self.related_ixps or [],
"evidence_refs": self.evidence_refs or [],
"created_at": to_iso8601_utc(self.created_at),
}

View File

@@ -0,0 +1,62 @@
"""BGP raw observation model for routing event ingestion."""
from sqlalchemy import Column, DateTime, ForeignKey, Index, Integer, JSON, String, Text
from sqlalchemy.sql import func
from app.core.time import to_iso8601_utc
from app.db.session import Base
class BGPObservation(Base):
__tablename__ = "bgp_observations"
id = Column(Integer, primary_key=True, index=True)
snapshot_id = Column(Integer, ForeignKey("data_snapshots.id"), nullable=True, index=True)
task_id = Column(Integer, ForeignKey("collection_tasks.id"), nullable=True, index=True)
source = Column(String(100), nullable=False, index=True)
ingest_batch_id = Column(String(100), nullable=True, index=True)
source_event_id = Column(String(100), nullable=True, index=True)
collector = Column(String(100), nullable=True, index=True)
peer_asn = Column(Integer, nullable=True, index=True)
peer_ip = Column(String(100), nullable=True)
prefix = Column(String(64), nullable=True, index=True)
event_type = Column(String(32), nullable=False, index=True)
as_path = Column(JSON, default=list)
origin_asn = Column(Integer, nullable=True, index=True)
next_hop = Column(String(100), nullable=True)
communities = Column(JSON, default=list)
observed_at = Column(DateTime(timezone=True), nullable=False, index=True)
collector_geo = Column(JSON, default=dict)
raw_payload = Column(JSON, default=dict)
created_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now(), index=True)
note = Column(Text, nullable=True)
__table_args__ = (
Index("idx_bgp_obs_source_observed", "source", "observed_at"),
Index("idx_bgp_obs_collector_prefix", "collector", "prefix"),
Index("idx_bgp_obs_task_source_event", "task_id", "source_event_id"),
)
def to_dict(self) -> dict:
return {
"id": self.id,
"snapshot_id": self.snapshot_id,
"task_id": self.task_id,
"source": self.source,
"ingest_batch_id": self.ingest_batch_id,
"source_event_id": self.source_event_id,
"collector": self.collector,
"peer_asn": self.peer_asn,
"peer_ip": self.peer_ip,
"prefix": self.prefix,
"event_type": self.event_type,
"as_path": self.as_path or [],
"origin_asn": self.origin_asn,
"next_hop": self.next_hop,
"communities": self.communities or [],
"observed_at": to_iso8601_utc(self.observed_at),
"collector_geo": self.collector_geo or {},
"raw_payload": self.raw_payload or {},
"created_at": to_iso8601_utc(self.created_at),
"note": self.note,
}