You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

229 lines
6.5 KiB

from django.db import models
from django.urls import reverse
from dncore.models import User
from .validators import HandleValidator, HandleValidatorWithSuffix
import ipaddress
class WhoisObject(models.Model):
class Meta:
abstract = True
handleSuffix = ""
handle = models.SlugField(max_length=32, unique=True, verbose_name='handle', validators=[HandleValidator()])
created = models.DateTimeField(auto_now_add=True)
last_modified = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.handle
def getClassName(self):
return self._meta.object_name
def genHandle(self, main=None):
if not main:
main = self.name
return self.genGenericHandle(main)
@classmethod
def genGenericHandle(clazz, main):
prefix = ""
if " " in main:
parts = main.split(" ")
prefix = "".join(map(lambda _x: _x[0], parts))
if len(prefix) < 3 and len(parts[-1]) > 1:
prefix += parts[-1][1:4 - len(prefix)]
else:
prefix = main[0:3]
prefix = prefix.upper()
i = 1
handle = "%s%%d-%s" % (prefix, clazz.handleSuffix)
while True:
try:
prefix
clazz.objects.get(handle=handle % i)
i += 1
except clazz.DoesNotExist:
break
return handle % i
def getNoDeleteReasons(self):
raise NotImplementedError()
def canBeDeleted(self):
return not bool(self.getNoDeleteReasons())
def handleAuto(self, name=None):
if self.handle == "AUTO":
self.handle = self.genHandle(name)
class Maintainer(WhoisObject):
handleSuffix = "MNT"
auth = models.ManyToManyField(User)
handle = models.SlugField(max_length=32, unique=True, verbose_name='handle', validators=[HandleValidatorWithSuffix('MNT')], help_text="Must end with -MNT, eg FOO3-MNT")
description = models.CharField(max_length=64, blank=True, help_text="Short description what this maintainer is for")
admin_c = models.ManyToManyField("Contact")
rir = models.BooleanField(default=False)
lir = models.BooleanField(default=False)
# autoInclude = models.BooleanField(default=True)
def get_absolute_url(self):
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
def getNoDeleteReasons(self):
reasons = []
mntables = [Contact, ASBlock, ASNumber, InetNum]
for mntable in mntables:
candidates = mntable.objects.filter(mnt_by=self).annotate(mntCount=models.Count('mnt_by')).filter(mntCount__lte=1)
for candidate in candidates:
reasons.append("Object %s would have no maintainers left." % candidate.handle)
return reasons
def canEdit(self, user):
return user in self.auth.all()
class MntdObject(WhoisObject):
class Meta:
abstract = True
mnt_by = models.ManyToManyField(Maintainer)
def canEdit(self, user):
mnts = user.maintainer_set.all()
objmnts = self.mnt_by.all()
if hasattr(self, "mnt_lower"):
objmnts |= self.mnt_lower.all()
for objmnt in objmnts:
if objmnt in mnts:
return True
return False
class Contact(MntdObject):
handleSuffix = "DN"
TYPE_PERSON = 'PERSON'
TYPE_ROLE = 'ROLE'
TYPE = (('person', TYPE_PERSON), ('role', TYPE_ROLE))
TYPE = (('person', TYPE_PERSON),)
name = models.CharField(max_length=128)
type = models.CharField(max_length=10, choices=TYPE, default=TYPE_PERSON)
def get_absolute_url(self):
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
def getNoDeleteReasons(self):
reasons = []
contactables = [Maintainer]
for contactable in contactables:
candidates = contactable.objects.filter(admin_c=self).annotate(contactCount=models.Count('admin_c')).filter(contactCount__lte=1)
for candidate in candidates:
reasons.append("Object %s would have no contact left." % candidate.handle)
return reasons
class ASBlock(MntdObject):
handleSuffix = "ASB"
parent_block = models.ForeignKey("ASBlock", models.CASCADE, null=True, blank=True, default=None)
name = models.CharField(max_length=32)
asBegin = models.PositiveIntegerField()
asEnd = models.PositiveIntegerField()
description = models.CharField(max_length=64, blank=True)
admin_c = models.ManyToManyField("Contact")
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_asblock_set', blank=True)
def getResource(self):
return "%s - %s" % (self.asBegin, self.asEnd)
def get_absolute_url(self):
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
def getNoDeleteReasons(self):
reasons = []
if self.asblock_set.count() > 0:
reasons.append("The AS block is referenced by the following other blocks: %s" % (", ".join(map(lambda _x: _x.handle, self.asblock_set.all()))))
if self.asnumber_set.count() > 0:
reasons.append("The AS block is referenced by the following as numbers: %s" % (", ".join(map(lambda _x: _x.handle, self.asnumber_set.all()))))
return reasons
class ASNumber(MntdObject):
handleSuffix = "AS"
number = models.PositiveIntegerField(unique=True, db_index=True)
volatile = models.BooleanField(default=False, help_text="Check if this AS is not going to be online 24/7 (for example on a laptop)")
asblock = models.ForeignKey(ASBlock, models.CASCADE)
name = models.CharField(max_length=32)
description = models.CharField(max_length=64, blank=True)
admin_c = models.ManyToManyField("Contact")
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_asnumber_set', blank=True)
def get_absolute_url(self):
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
def getNoDeleteReasons(self):
reasons = []
return reasons
def getResource(self):
return str(self.number)
class InetNum(MntdObject):
handleSuffix = "NET"
IPv4 = "ipv4"
IPv6 = "ipv6"
PROTO = ((IPv4, 'IPv4'), (IPv6, 'IPv6'))
protocol = models.CharField(max_length=4, choices=PROTO)
address = models.GenericIPAddressField(db_index=True)
netmask = models.PositiveIntegerField()
parent_range = models.ForeignKey("InetNum", models.CASCADE, null=True, blank=True, default=None)
name = models.CharField(max_length=64)
description = models.CharField(max_length=64, blank=True)
admin_c = models.ManyToManyField("Contact")
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_inetnum_set', blank=True)
def getResource(self):
return self.prefix()
def prefix(self):
""" Helper function, mainly used in templates """
return "%s/%s" % (self.address, self.netmask)
def getNetwork(self):
return ipaddress.ip_network(self.prefix())
def get_absolute_url(self):
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
def getNoDeleteReasons(self):
reasons = []
if self.inetnum_set.all().count() > 0:
reasons.append("The following networks depend on this network: %s" % ", ".join(map(lambda _x: _x.handle, self.inetnum_set.all())))
return reasons