Skip to content

Commit

Permalink
Toggled IXP active status based on active membership count
Browse files Browse the repository at this point in the history
  • Loading branch information
andy-isoc committed Nov 4, 2024
1 parent 34e5c77 commit ce0c160
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 0.6
- extend a membership if created date for imported record is before end date of previous record
- toggle IXP active status based on whether there are active memberships

## 0.5
- fix membership starting after it ended
Expand Down
29 changes: 26 additions & 3 deletions ixp_tracker/importers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

import requests
import dateutil.parser
from django.db.models import Q
from django_countries import countries

from ixp_tracker.conf import IXP_TRACKER_PEERING_DB_KEY, IXP_TRACKER_PEERING_DB_URL, DATA_ARCHIVE_URL
from ixp_tracker import models
from ixp_tracker.models import IXPMembershipRecord

logger = logging.getLogger("ixp_tracker")

Expand Down Expand Up @@ -42,6 +42,8 @@ def import_data(
logger.debug("Imported ASNs")
import_members(processing_date, geo_lookup)
logger.debug("Imported members")
toggle_ixp_active_status(processing_date)
logger.debug("Toggled IXPs active status")
else:
processing_date = processing_date.replace(day=1)
processing_month = processing_date.month
Expand All @@ -68,6 +70,8 @@ def import_data(
process_asn_data(geo_lookup)(asn_data)
member_data = backfill_data.get("netixlan", {"data": []}).get("data", [])
process_member_data(processing_date, geo_lookup)(member_data)
toggle_ixp_active_status(processing_date)
logger.debug("Toggled IXPs active status")


def get_data(endpoint: str, processor: Callable, limit: int = 0, last_updated: datetime = None) -> bool:
Expand Down Expand Up @@ -194,7 +198,7 @@ def do_process_member_data(all_member_data):
created_date = dateutil.parser.isoparse(member_data["created"]).date()
membership = models.IXPMembershipRecord.objects.filter(member=member).order_by("-start_date").first()
if created or membership is None:
membership = IXPMembershipRecord(
membership = models.IXPMembershipRecord(
member=member,
start_date=created_date,
is_rs_peer=member_data["is_rs_peer"],
Expand All @@ -216,7 +220,7 @@ def do_process_member_data(all_member_data):
membership.end_date = None
else:
# Most recent membership has ended so create a new membership record
membership = IXPMembershipRecord(
membership = models.IXPMembershipRecord(
member=member,
start_date=created_date,
is_rs_peer=member_data["is_rs_peer"],
Expand Down Expand Up @@ -265,3 +269,22 @@ def dedupe_member_data(raw_members_data):
deduped_data[member_key]["is_rs_peer"] = deduped_data[member_key]["is_rs_peer"] or raw_member["is_rs_peer"]
deduped_data[member_key]["speed"] += raw_member["speed"]
return list(deduped_data.values())


def toggle_ixp_active_status(processing_date: datetime):
for ixp in models.IXP.objects.all():
active_members = (models.IXPMembershipRecord.objects
.filter(member__in=ixp.ixpmember_set.all())
.filter(Q(end_date__isnull=True) | Q(end_date__gte=processing_date))
)
if ixp.active_status and len(active_members) == 0:
ixp.active_status = False
ixp.last_active = processing_date
ixp.save()
logger.debug("Marked IXP as inactive", extra={"ixp": ixp.peeringdb_id})
elif not ixp.active_status and len(active_members) > 0:
ixp.active_status = True
ixp.last_active = processing_date
ixp.save()
logger.debug("Marked IXP as active", extra={"ixp": ixp.peeringdb_id})
return
37 changes: 37 additions & 0 deletions tests/fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from datetime import datetime

from ixp_tracker.models import ASN, IXP


def create_asn_fixture(as_number: int, country: str = "CH"):
asn = ASN.objects.filter(number=as_number)
if len(asn) > 0:
return asn.first()
asn = ASN(
name="Network Org",
number=as_number,
peeringdb_id=5,
network_type="other",
registration_country_code=country,
created="2019-01-01",
last_updated="2024-05-01"
)
asn.save()
return asn


def create_ixp_fixture(peering_db_id: int, country = "MM"):
ixp = IXP(
name="Old name",
long_name="Network Name",
city="Aberdeen",
website="",
active_status=True,
peeringdb_id=peering_db_id,
country_code=country,
created=datetime(year=2020,month=10,day=1),
last_updated=datetime(year=2023,month=10,day=1),
last_active=datetime(year=2024, month=4, day=1)
)
ixp.save()
return ixp
37 changes: 2 additions & 35 deletions tests/test_members_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

from ixp_tracker import importers
from ixp_tracker.importers import ASNGeoLookup, dedupe_member_data
from ixp_tracker.models import ASN, IXP, IXPMember, IXPMembershipRecord
from ixp_tracker.models import IXPMember, IXPMembershipRecord
from tests.fixtures import create_asn_fixture, create_ixp_fixture

pytestmark = pytest.mark.django_db

Expand Down Expand Up @@ -397,37 +398,3 @@ def test_speed_for_deduped_members_is_sum_of_all_speeds():

deduped_member = deduped_data[0]
assert deduped_member["speed"] == 17000


def create_asn_fixture(as_number: int, country: str = "CH"):
asn = ASN.objects.filter(number=as_number)
if len(asn) > 0:
return asn.first()
asn = ASN(
name="Network Org",
number=as_number,
peeringdb_id=5,
network_type="other",
registration_country_code=country,
created="2019-01-01",
last_updated="2024-05-01"
)
asn.save()
return asn


def create_ixp_fixture(peering_db_id: int, country = "MM"):
ixp = IXP(
name="Old name",
long_name="Network Name",
city="Aberdeen",
website="",
active_status=True,
peeringdb_id=peering_db_id,
country_code=country,
created=datetime(year=2020,month=10,day=1),
last_updated=datetime(year=2023,month=10,day=1),
last_active=datetime(year=2024, month=4, day=1)
)
ixp.save()
return ixp
120 changes: 120 additions & 0 deletions tests/test_toggle_ixp_active_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import pytest
from datetime import datetime, timedelta, timezone

from ixp_tracker.importers import toggle_ixp_active_status
from ixp_tracker.models import IXP, IXPMember, IXPMembershipRecord
from tests.fixtures import create_asn_fixture, create_ixp_fixture

pytestmark = pytest.mark.django_db
processing_date = datetime.utcnow().replace(tzinfo=timezone.utc)


def test_active_ixp_with_members_remains_active():
ixp = create_ixp_fixture(1)
asn = create_asn_fixture(12345)
member = IXPMember(
ixp=ixp,
asn=asn,
last_updated=ixp.last_updated,
last_active=ixp.last_active
)
member.save()
membership = IXPMembershipRecord(
member=member,
start_date=ixp.created,
is_rs_peer=True,
speed=1000,
end_date=None
)
membership.save()

processing_date = datetime.utcnow().replace(tzinfo=timezone.utc)
toggle_ixp_active_status(processing_date)

ixp = IXP.objects.get(peeringdb_id=1)

assert ixp.active_status


def test_active_ixp_with_member_marked_ended_is_marked_inactive():
ixp = create_ixp_fixture(1)
asn = create_asn_fixture(12345)
member = IXPMember(
ixp=ixp,
asn=asn,
last_updated=ixp.last_updated,
last_active=ixp.last_active
)
member.save()
end_date = processing_date.replace(day=1) - timedelta(days=1)
membership = IXPMembershipRecord(
member=member,
start_date=ixp.created,
is_rs_peer=True,
speed=1000,
end_date=end_date
)
membership.save()

toggle_ixp_active_status(processing_date)

ixp = IXP.objects.get(peeringdb_id=1)

assert ixp.active_status is False


def test_inactive_ixp_with_no_active_members_remains_inactive():
ixp = create_ixp_fixture(1)
ixp.active_status = False
ixp.save()
asn = create_asn_fixture(12345)
member = IXPMember(
ixp=ixp,
asn=asn,
last_updated=ixp.last_updated,
last_active=ixp.last_active
)
member.save()
end_date = processing_date.replace(day=1) - timedelta(days=1)
membership = IXPMembershipRecord(
member=member,
start_date=ixp.created,
is_rs_peer=True,
speed=1000,
end_date=end_date
)
membership.save()

toggle_ixp_active_status(processing_date)

ixp = IXP.objects.get(peeringdb_id=1)

assert ixp.active_status is False


def test_inactive_ixp_with_active_member_marked_active():
ixp = create_ixp_fixture(1)
ixp.active_status = False
ixp.save()
asn = create_asn_fixture(12345)
member = IXPMember(
ixp=ixp,
asn=asn,
last_updated=ixp.last_updated,
last_active=ixp.last_active
)
member.save()
membership = IXPMembershipRecord(
member=member,
start_date=ixp.created,
is_rs_peer=True,
speed=1000,
end_date=None
)
membership.save()

toggle_ixp_active_status(processing_date)

ixp = IXP.objects.get(peeringdb_id=1)

assert ixp.active_status

0 comments on commit ce0c160

Please sign in to comment.