224 lines
7.3 KiB
Python
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)
|