extract BgpContext dataclass for build_bgp_intel

This commit is contained in:
Andrew Pimenov 2026-08-09 16:06:55 +03:00
parent d88b1bd95a
commit 5f51e70d5e
2 changed files with 58 additions and 48 deletions

View file

@ -108,6 +108,7 @@ import json
import os import os
import time import time
from pathlib import Path from pathlib import Path
from dataclasses import dataclass
from typing import Awaitable, Callable from typing import Awaitable, Callable
from netsleuth.models import IxpPresence from netsleuth.models import IxpPresence
@ -177,25 +178,28 @@ async def cached_json(
from netsleuth.models import BgpIntel from netsleuth.models import BgpIntel
def build_bgp_intel( @dataclass
asn: str, class BgpContext:
overview: dict, asn: str
neighbours: dict, overview: dict
prefixes: dict, neighbours: dict
updates: dict, prefixes: dict
asrank: dict | None, updates: dict
pdb_net: dict | None, asrank: dict | None
pdb_ixlan: dict | None, pdb_net: dict | None
timeframe_days: int, pdb_ixlan: dict | None
) -> BgpIntel: timeframe_days: int
info = parse_as_overview(overview)
upstreams, peers, downstreams = parse_asn_neighbours(neighbours)
announced, v4, v6 = parse_announced_prefixes(prefixes) def build_bgp_intel(ctx: BgpContext) -> BgpIntel:
events = parse_bgp_updates(updates) info = parse_as_overview(ctx.overview)
rank, cone_asns, cone_prefixes = parse_asrank(asrank) if asrank else (None, None, None) upstreams, peers, downstreams = parse_asn_neighbours(ctx.neighbours)
info_type, traffic, _net_id = parse_peeringdb_net(pdb_net) if pdb_net else (None, None, None) announced, v4, v6 = parse_announced_prefixes(ctx.prefixes)
events = parse_bgp_updates(ctx.updates)
rank, cone_asns, cone_prefixes = parse_asrank(ctx.asrank) if ctx.asrank else (None, None, None)
info_type, traffic, _net_id = parse_peeringdb_net(ctx.pdb_net) if ctx.pdb_net else (None, None, None)
return BgpIntel( return BgpIntel(
asn=asn, asn=ctx.asn,
holder=info["holder"], holder=info["holder"],
registry=info["registry"], registry=info["registry"],
allocated_at=info["allocated_at"], allocated_at=info["allocated_at"],
@ -206,8 +210,8 @@ def build_bgp_intel(
prefix_count_v4=v4, prefix_count_v4=v4,
prefix_count_v6=v6, prefix_count_v6=v6,
flaps=events, flaps=events,
stability=classify_stability(events, timeframe_days), stability=classify_stability(events, ctx.timeframe_days),
ixps=parse_peeringdb_netixlan(pdb_ixlan) if pdb_ixlan else [], ixps=parse_peeringdb_netixlan(ctx.pdb_ixlan) if ctx.pdb_ixlan else [],
pdb_info_type=info_type, pdb_info_type=info_type,
pdb_traffic=traffic, pdb_traffic=traffic,
asrank=rank, asrank=rank,
@ -284,6 +288,7 @@ async def collect_bgp(
pdb_ixlan = None pdb_ixlan = None
intel = build_bgp_intel( intel = build_bgp_intel(
BgpContext(
asn=asn, asn=asn,
overview=overview or {}, overview=overview or {},
neighbours=neighbours or {}, neighbours=neighbours or {},
@ -294,6 +299,7 @@ async def collect_bgp(
pdb_ixlan=pdb_ixlan, pdb_ixlan=pdb_ixlan,
timeframe_days=providers.ripestat_timeframe_days, timeframe_days=providers.ripestat_timeframe_days,
) )
)
raw = { raw = {
"ripestat-as-overview": overview, "ripestat-as-overview": overview,
"ripestat-asn-neighbours": neighbours, "ripestat-asn-neighbours": neighbours,

View file

@ -211,11 +211,12 @@ async def test_cached_json_survives_a_corrupt_cache_file(tmp_path: Path):
assert await cached_json(tmp_path, "k", ttl_hours=24, fetch=fetch) == {"value": "fresh"} assert await cached_json(tmp_path, "k", ttl_hours=24, fetch=fetch) == {"value": "fresh"}
from netsleuth.bgp import build_bgp_intel from netsleuth.bgp import BgpContext, build_bgp_intel
def test_build_bgp_intel_merges_every_source(api_fixture): def test_build_bgp_intel_merges_every_source(api_fixture):
intel = build_bgp_intel( intel = build_bgp_intel(
BgpContext(
asn="AS64500", asn="AS64500",
overview=api_fixture("ripestat_as_overview.json"), overview=api_fixture("ripestat_as_overview.json"),
neighbours=api_fixture("ripestat_asn_neighbours.json"), neighbours=api_fixture("ripestat_asn_neighbours.json"),
@ -226,6 +227,7 @@ def test_build_bgp_intel_merges_every_source(api_fixture):
pdb_ixlan=api_fixture("peeringdb_netixlan.json"), pdb_ixlan=api_fixture("peeringdb_netixlan.json"),
timeframe_days=14, timeframe_days=14,
) )
)
assert intel.asn == "AS64500" assert intel.asn == "AS64500"
assert intel.holder == "EXAMPLE-AS Example Telecom BV" assert intel.holder == "EXAMPLE-AS Example Telecom BV"
assert intel.upstreams == ["AS3356", "AS1299"] assert intel.upstreams == ["AS3356", "AS1299"]
@ -241,6 +243,7 @@ def test_build_bgp_intel_merges_every_source(api_fixture):
def test_build_bgp_intel_tolerates_every_optional_source_being_absent(api_fixture): def test_build_bgp_intel_tolerates_every_optional_source_being_absent(api_fixture):
intel = build_bgp_intel( intel = build_bgp_intel(
BgpContext(
asn="AS64500", asn="AS64500",
overview=api_fixture("ripestat_as_overview.json"), overview=api_fixture("ripestat_as_overview.json"),
neighbours={}, neighbours={},
@ -251,6 +254,7 @@ def test_build_bgp_intel_tolerates_every_optional_source_being_absent(api_fixtur
pdb_ixlan=None, pdb_ixlan=None,
timeframe_days=14, timeframe_days=14,
) )
)
assert intel.asn == "AS64500" assert intel.asn == "AS64500"
assert intel.upstreams == [] assert intel.upstreams == []
assert intel.asrank is None assert intel.asrank is None