fix: recover bgp anomaly and incident generation

This commit is contained in:
linkong
2026-03-31 18:17:55 +08:00
parent 6f01dfb590
commit 016507ad68
8 changed files with 289 additions and 58 deletions

View File

@@ -1,6 +1,6 @@
"""Tests for BGP observability helpers."""
from datetime import UTC, datetime
from datetime import UTC, datetime, timedelta
import pytest
from httpx import ASGITransport, AsyncClient
@@ -10,7 +10,10 @@ from app.api.v1.bgp import BGP_SOURCES
from app.core.security import get_current_user
from app.db.session import get_db
from app.main import app
from app.services.bgp_detectors import detect_mass_withdrawal_anomalies
from app.services.bgp_detectors import (
detect_mass_withdrawal_anomalies,
detect_origin_change_anomalies,
)
from app.services.collectors.bgp_common import (
create_bgp_anomalies_for_batch,
save_bgp_observations_for_batch,
@@ -223,6 +226,95 @@ def test_detect_mass_withdrawal_anomalies():
assert anomalies[0].prefix == "203.0.113.0/24"
def test_detect_origin_change_anomalies_creates_conflict_without_baseline():
events = [
{
"metadata": {
"prefix": "203.0.113.0/24",
"origin_asn": 64496,
"collector": "rrc00",
"collector_location": {
"country": "Netherlands",
"city": "Amsterdam",
"latitude": 52.3676,
"longitude": 4.9041,
},
}
},
{
"metadata": {
"prefix": "203.0.113.0/24",
"origin_asn": 64497,
"collector": "rrc01",
"collector_location": {
"country": "United Kingdom",
"city": "London",
"latitude": 51.5072,
"longitude": -0.1276,
},
}
},
]
anomalies = detect_origin_change_anomalies(
source="ris_live_bgp",
snapshot_id=1,
task_id=2,
events=events,
previous_origin_map={},
)
assert len(anomalies) == 2
assert {item.anomaly_type for item in anomalies} == {"origin_conflict"}
assert anomalies[0].peer_scope == ["rrc00", "rrc01"]
def test_detect_mass_withdrawal_anomalies_accepts_cross_collector_pair():
events = [
{
"metadata": {
"prefix": "203.0.113.0/24",
"origin_asn": 64496,
"event_type": "withdrawal",
"collector": "rrc00",
"peer_asn": 3333,
"collector_location": {
"country": "Netherlands",
"city": "Amsterdam",
"latitude": 52.3676,
"longitude": 4.9041,
},
}
},
{
"metadata": {
"prefix": "203.0.113.0/24",
"origin_asn": 64496,
"event_type": "withdrawal",
"collector": "rrc01",
"peer_asn": 3334,
"collector_location": {
"country": "United Kingdom",
"city": "London",
"latitude": 51.5072,
"longitude": -0.1276,
},
}
},
]
anomalies = detect_mass_withdrawal_anomalies(
source="ris_live_bgp",
snapshot_id=1,
task_id=2,
events=events,
)
assert len(anomalies) == 1
assert anomalies[0].severity == "medium"
assert anomalies[0].evidence["collector_count"] == 2
def test_bgp_incident_to_dict():
incident = BGPIncident(
source="ris_live_bgp",
@@ -405,6 +497,7 @@ async def test_infer_related_infrastructure_links_nearby_cables():
@pytest.mark.asyncio
async def test_build_bgp_collector_coverage_summarizes_observations():
now = datetime.now(UTC)
obs_one = BGPObservation(
source="ris_live_bgp",
collector="rrc00",
@@ -412,7 +505,7 @@ async def test_build_bgp_collector_coverage_summarizes_observations():
origin_asn=64496,
peer_asn=3333,
event_type="announcement",
observed_at=datetime(2026, 3, 30, 10, 0, tzinfo=UTC),
observed_at=now,
collector_geo={"city": "Amsterdam", "country": "Netherlands"},
)
obs_two = BGPObservation(
@@ -422,7 +515,7 @@ async def test_build_bgp_collector_coverage_summarizes_observations():
origin_asn=64497,
peer_asn=3334,
event_type="withdrawal",
observed_at=datetime(2026, 3, 30, 10, 5, tzinfo=UTC),
observed_at=now + timedelta(minutes=5),
collector_geo={"city": "Amsterdam", "country": "Netherlands"},
)
db = _FakeAsyncSession([[obs_one, obs_two]])
@@ -543,11 +636,22 @@ async def test_create_bgp_anomalies_for_batch_skips_existing_entity_keys():
extra_data={"prefix": "203.0.113.0/24", "origin_asn": 64496},
)
existing_key = ("origin_change:203.0.113.0/24:64497",)
existing_anomaly = BGPAnomaly(
source="ris_live_bgp",
anomaly_type="origin_change",
severity="critical",
status="active",
entity_key="origin_change:203.0.113.0/24:64497",
prefix="203.0.113.0/24",
origin_asn=64496,
new_origin_asn=64497,
)
db = _FakeAsyncSession([
[],
[],
[previous_record],
[existing_key],
[existing_anomaly],
])
events = [
{
@@ -578,7 +682,7 @@ async def test_create_bgp_anomalies_for_batch_skips_existing_entity_keys():
assert created == 0
assert len(db.added) == 0
incident_mock.assert_not_awaited()
incident_mock.assert_awaited_once()
async def _bgp_test_client(db_session):
@@ -740,6 +844,7 @@ async def test_bgp_event_summary_api_returns_aggregates():
@pytest.mark.asyncio
async def test_bgp_collectors_api_returns_coverage():
now = datetime.now(UTC)
observation = BGPObservation(
id=1,
source="ris_live_bgp",
@@ -748,7 +853,7 @@ async def test_bgp_collectors_api_returns_coverage():
prefix="203.0.113.0/24",
event_type="announcement",
origin_asn=64496,
observed_at=datetime(2026, 3, 30, 10, 0, tzinfo=UTC),
observed_at=now,
collector_geo={"city": "Amsterdam", "country": "Netherlands"},
)
db = _FakeAsyncSession([[observation], [observation]])