dnmapper/bgpdata/models.py

224 lines
7.3 KiB
Python

# This file is part of dnmapper, an AS--level mapping tool
# Licensed under GNU General Public License v3 or later
# Written by Sebastian Lohff (seba@someserver.de)
from django.db import models
from django.db.models import Q
# Create your models here.
class ConfigHost(models.Model):
CHECK_CHOICES = (
('CMK', "Check MK"),
('PLAIN', "Plain"),
('GOBGP', "GoBGP"),
)
# asno, ip, check method,
name = models.CharField(max_length=50)
number = models.IntegerField()
ip = models.GenericIPAddressField()
checkMethod = models.CharField(max_length=10, choices=CHECK_CHOICES)
def __str__(self):
return "%s (%s / %s)" % (self.name, self.number, self.ip)
class CrawlRun(models.Model):
# time start, time end,
startTime = models.DateTimeField()
endTime = models.DateTimeField(null=True, blank=True)
hostsCrawled = models.ManyToManyField(ConfigHost, blank=True)
graph = models.TextField()
asCount = models.IntegerField(default=0)
asOnlineCount = models.IntegerField(default=0)
asOfflineCount = models.IntegerField(default=0)
peeringCount = models.IntegerField(default=0)
def __str__(self):
return "Run %d - %s to %s" % (self.pk, self.startTime, self.endTime if self.endTime else "?")
def countAS(self):
return self.asCount
def countASOnline(self):
return self.asOnlineCount
def countASOffline(self):
return self.asOfflineCount
def countPeerings(self):
return self.peeringCount
class CrawlLog(models.Model):
INFO = 'INFO'
ERROR = 'ERROR'
DEBUG = 'DEBUG'
WARN = 'WARN'
SEVERITY = (
(INFO, 'info'),
(ERROR, 'error'),
(DEBUG, 'debug'),
(WARN, 'warning'),
)
crawl = models.ForeignKey(CrawlRun, on_delete=models.CASCADE)
host = models.ForeignKey(ConfigHost, null=True, blank=True, on_delete=models.SET_NULL)
logtime = models.DateTimeField(auto_now_add=True)
severity = models.CharField(max_length=10, choices=SEVERITY)
message = models.TextField()
@staticmethod
def log(crawl, msg, severity=None, host=None):
if not severity:
severity = CrawlLog.ERROR
log = CrawlLog()
log.crawl = crawl
log.message = msg
log.severity = severity
log.host = host
log.save()
def __str__(self):
host = "host %s - " % self.host.name if self.host else ""
return "Log %s %s: %s%s" % (self.get_severity_display(), self.logtime, host, self.message)
class AS(models.Model):
# asno
crawl = models.ForeignKey(CrawlRun, on_delete=models.CASCADE)
number = models.IntegerField(db_index=True)
directlyCrawled = models.BooleanField(default=False)
online = models.BooleanField(default=True, db_index=True)
lastSeen = models.ForeignKey(CrawlRun, blank=True, null=True, default=None, related_name='as_lastseen',
on_delete=models.CASCADE)
class Meta:
unique_together = (('crawl', 'number'),)
index_together = (
('crawl', 'number'),
)
def __str__(self):
return "AS %s (crawl %d)" % (self.number, self.crawl.pk)
def setOnline(self):
if not self.online:
self.online = True
self.lastSeen = None
self.save()
def getPeerings(self):
return Peering.objects.filter(Q(as1=self) | Q(as2=self))
def getAnnouncedPrefixes(self):
return list(set(map(lambda _x: "%(ip)s/%(prefix)s" % _x, self.announcement_set.all().values('ip', 'prefix'))))
def formatLastSeen(self):
if self.lastSeen:
return self.lastSeen.startTime.strftime("%d.%m.%Y %H:%I")
class BorderRouter(models.Model):
# as id, ip, check method, pingable, reachable
# unique: (crawl_id, asno, as id)
AS = models.ForeignKey(AS, on_delete=models.CASCADE)
routerID = models.GenericIPAddressField()
pingable = models.BooleanField(default=False)
reachable = models.BooleanField(default=False)
def __str__(self):
p = "p" if self.pingable else "!p"
r = "r" if self.reachable else "!r"
return "Router %s (AS %s, %s%s)" % (self.routerID, self.AS.number, p, r)
class Announcement(models.Model):
router = models.ForeignKey(BorderRouter, on_delete=models.CASCADE)
ip = models.GenericIPAddressField()
prefix = models.IntegerField()
# NOTE: increase length for longer pathes (currently supports a length of ~85)
ASPath = models.CharField(max_length=512)
nextHop = models.GenericIPAddressField()
originAS = models.ForeignKey(AS, null=True, on_delete=models.CASCADE)
crawlAS = models.ForeignKey(AS, related_name='crawl_as', null=True, on_delete=models.CASCADE)
def __str__(self):
return "%s/%s via %s (crawl %s)" % (self.ip, self.prefix, self.ASPath, self.router.AS.crawl.pk)
class Peering(models.Model):
DIRECT = 'direct'
PATH = 'path'
ORIGIN = (
(PATH, 'BGP Path'),
(DIRECT, 'Direct Connection'),
)
index_together = (
('as1', 'as2'),
)
as1 = models.ForeignKey(AS, related_name='peering1', on_delete=models.CASCADE)
as2 = models.ForeignKey(AS, related_name='peering2', on_delete=models.CASCADE)
origin = models.CharField(max_length=10, choices=ORIGIN)
def __str__(self):
return "AS %s <--> AS %s (%s, crawl %s)" % (self.as1.number, self.as2.number,
self.get_origin_display(), self.as1.crawl.pk)
def containsAS(self, AS):
return AS in (self.as1, self.as2)
@staticmethod
def getPeering(as1, as2):
""" Find matching peering """
try:
return Peering.objects.get(as1=as1, as2=as2)
except Peering.DoesNotExist:
return Peering.objects.get(as1=as2, as2=as1)
class BorderRouterPair(models.Model):
peering = models.ForeignKey(Peering, on_delete=models.CASCADE)
router1 = models.ForeignKey(BorderRouter, default=None, blank=True, null=True, related_name='routerpair1',
on_delete=models.CASCADE)
router2 = models.ForeignKey(BorderRouter, default=None, blank=True, null=True, related_name='routerpair2',
on_delete=models.CASCADE)
def __str__(self):
return "%s <--> %s (crawl %d)" % (self.router1, self.router2, self.router1.AS.crawl.pk)
@staticmethod
def getPairing(peering, router1, router2):
try:
return BorderRouterPair.objects.get(peering=peering, router1=router1, router2=router2)
except BorderRouterPair.DoesNotExist:
return BorderRouterPair.objects.get(peering=peering, router1=router2, router2=router1)
class ASLastSeen(models.Model):
asn = models.IntegerField(db_index=True, unique=True)
directlyCrawled = models.BooleanField(default=False)
online = models.BooleanField()
lastSeen = models.DateTimeField(blank=True, null=True)
crawlLastSeen = models.ForeignKey(CrawlRun, null=True, on_delete=models.SET_NULL)
def __str__(self):
return ("AS{} {}, last seen {} (crawl {})"
.format(self.asn, "online" if self.online else "offline", self.lastSeen, self.crawlLastSeen.pk))
class ASLastSeenNeighbor(models.Model):
asn = models.IntegerField()
neighbor = models.ForeignKey(ASLastSeen, on_delete=models.CASCADE)