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 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:mnt-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 class MntdObject(WhoisObject): class Meta: abstract = True mnt_by = models.ManyToManyField(Maintainer) 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:contact-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 get_absolute_url(self): return reverse("whoisdb:asblock-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:asnumber-detail", kwargs={"handle": self.handle}) def getNoDeleteReasons(self): reasons = [] return reasons 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 getNetwork(self): return ipaddress.ip_network("%s/%s" % (self.address, self.netmask)) def get_absolute_url(self): return reverse("whoisdb:inetnum-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