dnmgmt/whoisdb/models.py

278 lines
8.1 KiB
Python
Raw Normal View History

2017-02-10 02:45:45 +01:00
from django.db import models
2017-03-21 02:36:07 +01:00
from django.db.models import Q
2017-02-28 19:44:15 +01:00
from django.urls import reverse
2017-02-10 02:45:45 +01:00
from dncore.models import User
2017-02-24 03:43:59 +01:00
from .validators import HandleValidator, HandleValidatorWithSuffix
2017-02-10 02:45:45 +01:00
2017-03-01 03:21:03 +01:00
import ipaddress
2017-02-15 02:35:46 +01:00
class WhoisObject(models.Model):
2017-02-24 03:43:59 +01:00
class Meta:
abstract = True
handleSuffix = ""
2017-02-15 02:35:46 +01:00
2017-02-23 22:28:06 +01:00
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)
2017-02-15 02:35:46 +01:00
2017-03-22 04:06:24 +01:00
#def __init__(self, *args, **kwargs):
# super(WhoisObject, self).__init__(*args, **kwargs)
# if getattr(self, "handle"):
# field = self._meta.get_field("handle")
# if HandleValidatorWithSuffix not in map(type, field.validators):
# print(self.handle, "NOOOOT")
# field.validators.append(HandleValidatorWithSuffix(self.handleSuffix))
# else:
# print(self.handle, list(map(type, field.validators)))
def getPK(self):
return self.handle
2017-02-24 03:43:59 +01:00
def __str__(self):
return self.handle
2017-03-31 03:26:10 +02:00
def getAppName(self):
return "whoisdb"
2017-03-20 00:16:33 +01:00
def getClassName(self):
return self._meta.object_name
2017-02-24 03:43:59 +01:00
def genHandle(self, main=None):
if not main:
main = self.name
2017-02-28 19:44:15 +01:00
return self.genGenericHandle(main)
@classmethod
def genGenericHandle(clazz, main):
2017-02-24 03:43:59 +01:00
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
2017-02-28 19:44:15 +01:00
handle = "%s%%d-%s" % (prefix, clazz.handleSuffix)
2017-02-24 03:43:59 +01:00
while True:
try:
prefix
2017-02-28 19:44:15 +01:00
clazz.objects.get(handle=handle % i)
2017-02-24 03:43:59 +01:00
i += 1
2017-02-28 19:44:15 +01:00
except clazz.DoesNotExist:
2017-02-24 03:43:59 +01:00
break
return handle % i
2017-02-28 19:44:15 +01:00
def getNoDeleteReasons(self):
2017-03-22 19:11:19 +01:00
raise NotImplementedError("Delete reason checking is not implemented for this model")
2017-02-28 19:44:15 +01:00
def canBeDeleted(self):
return not bool(self.getNoDeleteReasons())
2017-02-24 03:43:59 +01:00
def handleAuto(self, name=None):
if self.handle == "AUTO":
self.handle = self.genHandle(name)
2017-02-15 02:35:46 +01:00
class Maintainer(WhoisObject):
2017-02-24 03:43:59 +01:00
handleSuffix = "MNT"
2017-02-10 02:45:45 +01:00
auth = models.ManyToManyField(User)
2017-02-28 19:44:15 +01:00
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")
2017-02-15 02:35:46 +01:00
2017-03-27 02:40:01 +02:00
admin_c = models.ManyToManyField("Contact", verbose_name="Administrative Contact")
2017-02-15 02:35:46 +01:00
2017-03-14 20:18:57 +01:00
rir = models.BooleanField(default=False)
lir = models.BooleanField(default=False)
2017-02-24 03:43:59 +01:00
# autoInclude = models.BooleanField(default=True)
2017-02-28 19:44:15 +01:00
def get_absolute_url(self):
2017-03-20 00:16:33 +01:00
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
2017-02-28 19:44:15 +01:00
def getNoDeleteReasons(self):
reasons = []
# FIXME: Tempfix for circular dependency problem
import domains.models
mntables = [Contact, ASBlock, ASNumber, InetNum, domains.models.Domain, domains.models.Nameserver]
2017-02-28 19:44:15 +01:00
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)
2017-03-01 03:21:03 +01:00
return reasons
2017-02-15 02:35:46 +01:00
2017-03-20 00:16:33 +01:00
def canEdit(self, user):
return user in self.auth.all()
2017-02-24 03:43:59 +01:00
class MntdObject(WhoisObject):
2017-02-15 02:35:46 +01:00
class Meta:
abstract = True
mnt_by = models.ManyToManyField(Maintainer, help_text="You can select multiple maintainers here")
2017-02-24 03:43:59 +01:00
2017-03-20 00:16:33 +01:00
def canEdit(self, user):
2017-03-27 13:36:03 +02:00
if not hasattr(user, "maintainer_set"):
return False
2017-03-20 00:16:33 +01:00
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
2017-03-21 02:36:07 +01:00
@classmethod
def getMntQueryset(clazz, mnts, instance, attr=None):
mntQ = Q(mnt_by__in=mnts)
if hasattr(clazz, "mnt_lower"):
mntQ |= Q(mnt_lower__in=mnts)
qs = clazz.objects.filter(mntQ)
if attr and instance and instance.pk:
2017-03-22 04:06:24 +01:00
if type(instance._meta.get_field(attr)) == models.ManyToManyField:
2017-03-21 02:36:07 +01:00
qs |= getattr(instance, attr).all()
2017-03-22 04:06:24 +01:00
elif getattr(instance, attr) is not None:
qs |= clazz.objects.filter(pk=getattr(instance, attr).pk)
2017-03-21 02:36:07 +01:00
return qs.distinct()
2017-02-15 02:35:46 +01:00
2017-03-22 04:06:24 +01:00
2017-02-15 02:35:46 +01:00
class Contact(MntdObject):
2017-02-24 03:43:59 +01:00
handleSuffix = "DN"
2017-02-28 19:44:15 +01:00
TYPE_PERSON = 'PERSON'
TYPE_ROLE = 'ROLE'
TYPE = (('person', TYPE_PERSON), ('role', TYPE_ROLE))
TYPE = (('person', TYPE_PERSON),)
2017-02-15 02:35:46 +01:00
name = models.CharField(max_length=128)
2017-03-14 20:18:57 +01:00
type = models.CharField(max_length=10, choices=TYPE, default=TYPE_PERSON)
2017-02-28 19:44:15 +01:00
def get_absolute_url(self):
2017-03-20 00:16:33 +01:00
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
2017-02-15 02:35:46 +01:00
2017-02-28 19:44:15 +01:00
def getNoDeleteReasons(self):
reasons = []
2017-03-27 13:29:32 +02:00
contactables = [Maintainer, ASBlock, ASNumber, InetNum]
2017-02-28 19:44:15 +01:00
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)
2017-03-01 03:21:03 +01:00
2017-02-28 19:44:15 +01:00
return reasons
2017-02-24 03:43:59 +01:00
2017-03-01 03:21:03 +01:00
2017-02-21 20:45:44 +01:00
class ASBlock(MntdObject):
2017-02-24 03:43:59 +01:00
handleSuffix = "ASB"
2017-02-21 20:45:44 +01:00
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()
2017-02-21 20:45:44 +01:00
description = models.CharField(max_length=64, blank=True)
admin_c = models.ManyToManyField("Contact")
2017-02-15 02:35:46 +01:00
2017-02-28 19:44:15 +01:00
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_asblock_set', blank=True)
2017-02-24 03:43:59 +01:00
2017-03-27 12:18:02 +02:00
def contains(self, block):
return self.asBegin <= block.asBegin <= self.asEnd and self.asBegin <= block.asEnd <= self.asEnd
2017-03-20 00:16:33 +01:00
def getResource(self):
return "%s - %s" % (self.asBegin, self.asEnd)
def get_absolute_url(self):
2017-03-20 00:16:33 +01:00
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
2017-03-01 03:21:03 +01:00
2017-03-15 03:07:43 +01:00
2017-02-21 20:45:44 +01:00
class ASNumber(MntdObject):
2017-02-24 03:43:59 +01:00
handleSuffix = "AS"
2017-02-15 02:35:46 +01:00
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)")
2017-02-21 20:45:44 +01:00
asblock = models.ForeignKey(ASBlock, models.CASCADE)
name = models.CharField(max_length=32)
2017-02-21 20:45:44 +01:00
description = models.CharField(max_length=64, blank=True)
admin_c = models.ManyToManyField("Contact")
2017-02-21 20:45:44 +01:00
2017-02-28 19:44:15 +01:00
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_asnumber_set', blank=True)
2017-02-15 02:35:46 +01:00
def get_absolute_url(self):
2017-03-20 00:16:33 +01:00
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
def getNoDeleteReasons(self):
reasons = []
return reasons
2017-02-15 02:35:46 +01:00
2017-03-20 00:16:33 +01:00
def getResource(self):
return str(self.number)
2017-03-15 03:07:43 +01:00
2017-03-22 04:06:24 +01:00
2017-02-28 19:44:15 +01:00
class InetNum(MntdObject):
2017-03-27 00:33:29 +02:00
class Meta:
unique_together = (
("address", "netmask"),
)
2017-02-24 03:43:59 +01:00
handleSuffix = "NET"
2017-02-28 19:44:15 +01:00
IPv4 = "ipv4"
IPv6 = "ipv6"
PROTO = ((IPv4, 'IPv4'), (IPv6, 'IPv6'))
2017-02-15 02:35:46 +01:00
protocol = models.CharField(max_length=4, choices=PROTO)
2017-02-28 19:44:15 +01:00
address = models.GenericIPAddressField(db_index=True)
2017-02-15 02:35:46 +01:00
netmask = models.PositiveIntegerField()
2017-02-21 20:45:44 +01:00
parent_range = models.ForeignKey("InetNum", models.CASCADE, null=True, blank=True, default=None)
2017-03-01 03:21:03 +01:00
name = models.CharField(max_length=64)
2017-02-21 20:45:44 +01:00
description = models.CharField(max_length=64, blank=True)
2017-03-27 00:33:29 +02:00
origin_as = models.ManyToManyField(ASNumber, blank=True)
admin_c = models.ManyToManyField("Contact")
2017-02-15 02:35:46 +01:00
2017-02-28 19:44:15 +01:00
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_inetnum_set', blank=True)
2017-03-01 03:21:03 +01:00
2017-03-20 00:16:33 +01:00
def getResource(self):
return self.prefix()
2017-03-15 03:07:43 +01:00
def prefix(self):
""" Helper function, mainly used in templates """
return "%s/%s" % (self.address, self.netmask)
2017-03-01 03:21:03 +01:00
def getNetwork(self):
2017-03-15 03:07:43 +01:00
return ipaddress.ip_network(self.prefix())
2017-03-01 03:21:03 +01:00
def get_absolute_url(self):
2017-03-20 00:16:33 +01:00
return reverse("whoisdb:handle-detail", kwargs={"handle": self.handle})
2017-03-01 03:21:03 +01:00
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