feat: expand bgp observability surfaces

This commit is contained in:
linkong
2026-03-31 14:07:28 +08:00
parent ac63bba2a2
commit 552e49bde0
16 changed files with 1319 additions and 47 deletions

View File

@@ -16,7 +16,11 @@ from app.services.collectors.bgp_common import (
save_bgp_observations_for_batch,
)
from app.services.bgp_enrichment import enrich_bgp_events_for_batch, extract_bgp_network_fields
from app.services.bgp_incidents import create_bgp_incidents_for_anomalies
from app.services.bgp_incidents import (
create_bgp_incidents_for_anomalies,
infer_related_infrastructure,
)
from app.services.bgp_collectors import build_bgp_collector_coverage
from app.models.bgp_anomaly import BGPAnomaly
from app.models.collected_data import CollectedData
from app.models.bgp_incident import BGPIncident
@@ -331,13 +335,17 @@ async def test_create_bgp_incidents_for_anomalies_aggregates_regions_and_collect
},
)
created = await create_bgp_incidents_for_anomalies(
db,
source="ris_live_bgp",
snapshot_id=1,
task_id=2,
anomalies=[anomaly],
)
with patch(
"app.services.bgp_incidents.infer_related_infrastructure",
new=AsyncMock(return_value={"related_cables": [], "related_ixps": []}),
):
created = await create_bgp_incidents_for_anomalies(
db,
source="ris_live_bgp",
snapshot_id=1,
task_id=2,
anomalies=[anomaly],
)
assert created == 1
assert db.commits == 1
@@ -348,6 +356,91 @@ async def test_create_bgp_incidents_for_anomalies_aggregates_regions_and_collect
assert incident.affected_regions[0]["city"] == "Amsterdam"
@pytest.mark.asyncio
async def test_infer_related_infrastructure_links_nearby_cables():
landing = CollectedData(
source="arcgis_landing_points",
name="Amsterdam Landing",
data_type="landing_point",
extra_data={
"city_id": 10,
"country": "Netherlands",
"city": "Amsterdam",
"latitude": 52.3676,
"longitude": 4.9041,
},
)
relation = CollectedData(
source="arcgis_cable_landing_relation",
name="rel-1",
data_type="landing_relation",
extra_data={"city_id": 10, "cable_id": 20},
)
cable = CollectedData(
source="arcgis_cables",
name="AEConnect-1",
data_type="cable",
extra_data={"cable_id": 20},
)
db = _FakeAsyncSession([[landing], [relation], [cable]])
result = await infer_related_infrastructure(
db,
[
{
"collector": "rrc00",
"country": "Netherlands",
"city": "Amsterdam",
"latitude": 52.36,
"longitude": 4.90,
}
],
)
assert len(result["related_cables"]) == 1
assert result["related_cables"][0]["landing_point"] == "Amsterdam Landing"
assert result["related_cables"][0]["cable_names"] == ["AEConnect-1"]
assert result["related_ixps"][0]["name"] == "Amsterdam, Netherlands"
@pytest.mark.asyncio
async def test_build_bgp_collector_coverage_summarizes_observations():
obs_one = BGPObservation(
source="ris_live_bgp",
collector="rrc00",
prefix="203.0.113.0/24",
origin_asn=64496,
peer_asn=3333,
event_type="announcement",
observed_at=datetime(2026, 3, 30, 10, 0, tzinfo=UTC),
collector_geo={"city": "Amsterdam", "country": "Netherlands"},
)
obs_two = BGPObservation(
source="ris_live_bgp",
collector="rrc00",
prefix="198.51.100.0/24",
origin_asn=64497,
peer_asn=3334,
event_type="withdrawal",
observed_at=datetime(2026, 3, 30, 10, 5, tzinfo=UTC),
collector_geo={"city": "Amsterdam", "country": "Netherlands"},
)
db = _FakeAsyncSession([[obs_one, obs_two]])
coverage = await build_bgp_collector_coverage(db, source_filter=BGP_SOURCES)
first = next(item for item in coverage if item["collector"] == "rrc00")
assert first["observation_count"] == 2
assert first["recent_24h_observation_count"] == 2
assert first["recent_7d_observation_count"] == 2
assert first["prefix_count"] == 2
assert first["recent_24h_prefix_count"] == 2
assert first["origin_asn_count"] == 2
assert first["latest_event_type"] == "withdrawal"
assert first["baseline_scope"]["countries"] == ["Netherlands"]
assert first["baseline_scope"]["cities"] == ["Amsterdam"]
@pytest.mark.asyncio
async def test_save_bgp_observations_for_batch_adds_rows():
db = _FakeAsyncSession([])
@@ -643,3 +736,41 @@ async def test_bgp_event_summary_api_returns_aggregates():
assert payload["prefix_count"] == 2
assert payload["by_type"]["announcement"] == 1
assert payload["by_type"]["withdrawal"] == 1
@pytest.mark.asyncio
async def test_bgp_collectors_api_returns_coverage():
observation = BGPObservation(
id=1,
source="ris_live_bgp",
collector="rrc00",
peer_asn=3333,
prefix="203.0.113.0/24",
event_type="announcement",
origin_asn=64496,
observed_at=datetime(2026, 3, 30, 10, 0, tzinfo=UTC),
collector_geo={"city": "Amsterdam", "country": "Netherlands"},
)
db = _FakeAsyncSession([[observation], [observation]])
client = await _bgp_test_client(db)
try:
list_response = await client.get("/api/v1/bgp/collectors")
summary_response = await client.get("/api/v1/bgp/collectors/summary")
finally:
await client.aclose()
app.dependency_overrides.clear()
assert list_response.status_code == 200
list_payload = list_response.json()
assert list_payload["total"] >= 1
target = next(item for item in list_payload["data"] if item["collector"] == "rrc00")
assert target["observation_count"] == 1
assert target["prefix_count"] == 1
assert summary_response.status_code == 200
summary_payload = summary_response.json()
assert summary_payload["active_collectors"] >= 1
assert summary_payload["observed_prefixes"] >= 1
assert summary_payload["recent_24h_events"] >= 1
assert summary_payload["recent_7d_events"] >= 1