feat(bgp): improve prefix-geo pipeline and collector reliability
This commit is contained in:
@@ -35,6 +35,8 @@ from app.models.user import User
|
||||
from app.services.collectors.bgp_common import normalize_bgp_event
|
||||
from app.services.collectors.bgpstream import BGPStreamBackfillCollector
|
||||
from app.services.collectors.iptoasn import IPtoASNPrefixGeoCollector
|
||||
from app.services.collectors.opengeofeed import OpenGeoFeedPrefixGeoCollector
|
||||
from app.services.collectors.nro_delegated import NRODelegatedPrefixGeoCollector
|
||||
|
||||
|
||||
class _FakeScalarResult:
|
||||
@@ -170,6 +172,75 @@ def test_iptoasn_transform_creates_prefix_geography_records():
|
||||
assert record["metadata"]["source_dataset"] == "iptoasn_combined"
|
||||
|
||||
|
||||
def test_iptoasn_build_dataset_urls_from_combined():
|
||||
urls = IPtoASNPrefixGeoCollector._build_dataset_urls(
|
||||
"https://iptoasn.com/data/ip2asn-combined.tsv.gz"
|
||||
)
|
||||
assert urls == [
|
||||
"https://iptoasn.com/data/ip2asn-v4.tsv.gz",
|
||||
"https://iptoasn.com/data/ip2asn-v6.tsv.gz",
|
||||
]
|
||||
|
||||
|
||||
def test_iptoasn_build_dataset_urls_passthrough_non_combined():
|
||||
url = "https://example.com/custom.tsv.gz"
|
||||
urls = IPtoASNPrefixGeoCollector._build_dataset_urls(url)
|
||||
assert urls == [url]
|
||||
|
||||
|
||||
def test_opengeofeed_transform_creates_prefix_geography_records():
|
||||
collector = OpenGeoFeedPrefixGeoCollector()
|
||||
transformed = collector.transform(
|
||||
[
|
||||
{
|
||||
"prefix": "203.0.113.0/24",
|
||||
"country_code": "GB",
|
||||
"region": "GB-LND",
|
||||
"city": "London",
|
||||
"postal_code": "EC1A",
|
||||
"extra_columns": ["source:example"],
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
assert len(transformed) == 1
|
||||
record = transformed[0]
|
||||
assert record["name"] == "203.0.113.0/24"
|
||||
assert record["metadata"]["family"] == "ipv4"
|
||||
assert record["metadata"]["range_start"] == "203.0.113.0"
|
||||
assert record["metadata"]["range_end"] == "203.0.113.255"
|
||||
assert record["metadata"]["country_code"] == "GB"
|
||||
assert record["metadata"]["source_dataset"] == "opengeofeed_public"
|
||||
assert record["metadata"]["confidence"] == "geofeed"
|
||||
|
||||
|
||||
def test_nro_delegated_transform_creates_prefix_geography_records():
|
||||
collector = NRODelegatedPrefixGeoCollector()
|
||||
transformed = collector.transform(
|
||||
[
|
||||
{
|
||||
"rir": "ripencc",
|
||||
"country_code": "DE",
|
||||
"type": "ipv4",
|
||||
"start": "198.51.100.0",
|
||||
"value": "256",
|
||||
"allocated_date": "20250401",
|
||||
"status": "allocated",
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
assert len(transformed) == 1
|
||||
record = transformed[0]
|
||||
assert record["name"] == "198.51.100.0/24"
|
||||
assert record["metadata"]["family"] == "ipv4"
|
||||
assert record["metadata"]["range_start"] == "198.51.100.0"
|
||||
assert record["metadata"]["range_end"] == "198.51.100.255"
|
||||
assert record["metadata"]["country_code"] == "DE"
|
||||
assert record["metadata"]["source_dataset"] == "nro_delegated_stats"
|
||||
assert record["metadata"]["confidence"] == "registry_allocated"
|
||||
|
||||
|
||||
def test_bgp_anomaly_to_dict():
|
||||
anomaly = BGPAnomaly(
|
||||
source="ris_live_bgp",
|
||||
@@ -605,7 +676,7 @@ async def test_enrich_bgp_events_for_batch_adds_profiles_and_prefix_scope():
|
||||
}
|
||||
}
|
||||
|
||||
db = _FakeAsyncSession([[historical_observation], [iptoasn_row], [peeringdb_record]])
|
||||
db = _FakeAsyncSession([[historical_observation], [], [iptoasn_row], [peeringdb_record]])
|
||||
events = [
|
||||
{
|
||||
"metadata": {
|
||||
@@ -639,6 +710,90 @@ async def test_enrich_bgp_events_for_batch_adds_profiles_and_prefix_scope():
|
||||
assert enrichment["prefix_scope"]["cities"] == ["London"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enrich_bgp_events_for_batch_prefers_opengeofeed_over_iptoasn():
|
||||
opengeofeed_row = {
|
||||
"extra_data": {
|
||||
"family": "ipv4",
|
||||
"range_start": "203.0.113.0",
|
||||
"range_end": "203.0.113.255",
|
||||
"prefix": "203.0.113.0/24",
|
||||
"country_code": "GB",
|
||||
"region": "GB-LND",
|
||||
"city": "London",
|
||||
"source_dataset": "opengeofeed_public",
|
||||
"confidence": "geofeed",
|
||||
}
|
||||
}
|
||||
db = _FakeAsyncSession([[], [opengeofeed_row], []])
|
||||
events = [
|
||||
{
|
||||
"metadata": {
|
||||
"prefix": "203.0.113.0/24",
|
||||
"origin_asn": 64497,
|
||||
"collector": "rrc00",
|
||||
"collector_location": {
|
||||
"country": "Netherlands",
|
||||
"city": "Amsterdam",
|
||||
"latitude": 52.3676,
|
||||
"longitude": 4.9041,
|
||||
},
|
||||
"as_path": [3333, 64497],
|
||||
"timestamp": "2026-03-30T10:00:00Z",
|
||||
},
|
||||
"reference_date": "2026-03-30T10:00:00Z",
|
||||
}
|
||||
]
|
||||
|
||||
enriched = await enrich_bgp_events_for_batch(db, source="ris_live_bgp", events=events)
|
||||
geography = enriched[0]["metadata"]["enrichment"]["prefix_geography"]
|
||||
assert geography["source"] == "opengeofeed_public"
|
||||
assert geography["confidence"] == "geofeed"
|
||||
assert geography["city"] == "London"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_enrich_bgp_events_for_batch_falls_back_to_nro_delegated():
|
||||
nro_row = {
|
||||
"extra_data": {
|
||||
"family": "ipv4",
|
||||
"range_start": "198.51.100.0",
|
||||
"range_end": "198.51.100.255",
|
||||
"prefix": "198.51.100.0/24",
|
||||
"country_code": "DE",
|
||||
"rir": "ripencc",
|
||||
"source_dataset": "nro_delegated_stats",
|
||||
"confidence": "registry_allocated",
|
||||
}
|
||||
}
|
||||
|
||||
db = _FakeAsyncSession([[], [], [nro_row], []])
|
||||
events = [
|
||||
{
|
||||
"metadata": {
|
||||
"prefix": "198.51.100.0/24",
|
||||
"origin_asn": 64512,
|
||||
"collector": "rrc00",
|
||||
"collector_location": {
|
||||
"country": "Netherlands",
|
||||
"city": "Amsterdam",
|
||||
"latitude": 52.3676,
|
||||
"longitude": 4.9041,
|
||||
},
|
||||
"as_path": [3333, 64512],
|
||||
"timestamp": "2026-03-30T10:00:00Z",
|
||||
},
|
||||
"reference_date": "2026-03-30T10:00:00Z",
|
||||
}
|
||||
]
|
||||
|
||||
enriched = await enrich_bgp_events_for_batch(db, source="ris_live_bgp", events=events)
|
||||
geography = enriched[0]["metadata"]["enrichment"]["prefix_geography"]
|
||||
assert geography["source"] == "nro_delegated_stats"
|
||||
assert geography["confidence"] == "registry_allocated"
|
||||
assert geography["country"] == "德国"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_bgp_incidents_for_anomalies_aggregates_regions_and_collectors():
|
||||
db = _FakeAsyncSession([[]])
|
||||
|
||||
Reference in New Issue
Block a user