diff options
Diffstat (limited to 'mirrors/models.py')
-rw-r--r-- | mirrors/models.py | 165 |
1 files changed, 138 insertions, 27 deletions
diff --git a/mirrors/models.py b/mirrors/models.py index 85423303..9743d177 100644 --- a/mirrors/models.py +++ b/mirrors/models.py @@ -1,78 +1,189 @@ +from datetime import timedelta +import socket +from urlparse import urlparse + +from django.core.exceptions import ValidationError from django.db import models +from django.db.models.signals import pre_save +from django_countries.fields import CountryField + +from .fields import IPNetworkField +from main.utils import set_created_field -TIER_CHOICES = ( - (0, 'Tier 0'), - (1, 'Tier 1'), - (2, 'Tier 2'), - (-1, 'Untiered'), -) class Mirror(models.Model): - name = models.CharField(max_length=255) + TIER_CHOICES = ( + (0, 'Tier 0'), + (1, 'Tier 1'), + (2, 'Tier 2'), + (-1, 'Untiered'), + ) + + name = models.CharField(max_length=255, unique=True) tier = models.SmallIntegerField(default=2, choices=TIER_CHOICES) - upstream = models.ForeignKey('self', null=True) - country = models.CharField(max_length=255, db_index=True) + upstream = models.ForeignKey('self', null=True, on_delete=models.SET_NULL) admin_email = models.EmailField(max_length=255, blank=True) + alternate_email = models.EmailField(max_length=255, blank=True) public = models.BooleanField(default=True) active = models.BooleanField(default=True) - isos = models.BooleanField(default=True) + isos = models.BooleanField("ISOs", default=True) rsync_user = models.CharField(max_length=50, blank=True, default='') rsync_password = models.CharField(max_length=50, blank=True, default='') + bug = models.PositiveIntegerField("Flyspray bug", null=True, blank=True) notes = models.TextField(blank=True) + created = models.DateTimeField(editable=False) + last_modified = models.DateTimeField(editable=False) class Meta: - ordering = ('country', 'name') + ordering = ('name',) def __unicode__(self): return self.name - def supported_protocols(self): - protocols = MirrorProtocol.objects.filter( - urls__mirror=self).order_by('protocol').distinct() - return ", ".join([p.protocol for p in protocols]) - def downstream(self): return Mirror.objects.filter(upstream=self).order_by('name') def get_absolute_url(self): return '/mirrors/%s/' % self.name + class MirrorProtocol(models.Model): protocol = models.CharField(max_length=10, unique=True) + is_download = models.BooleanField(default=True, + help_text="Is protocol useful for end-users, e.g. HTTP") + default = models.BooleanField(default=True, + help_text="Included by default when building mirror list?") + created = models.DateTimeField(editable=False) + def __unicode__(self): return self.protocol + class Meta: - verbose_name = 'Mirror Protocol' + ordering = ('protocol',) + class MirrorUrl(models.Model): - url = models.CharField(max_length=255) - protocol = models.ForeignKey(MirrorProtocol, related_name="urls") + url = models.CharField("URL", max_length=255, unique=True) + protocol = models.ForeignKey(MirrorProtocol, related_name="urls", + editable=False, on_delete=models.PROTECT) mirror = models.ForeignKey(Mirror, related_name="urls") + country = CountryField(blank=True, db_index=True) + has_ipv4 = models.BooleanField("IPv4 capable", default=True, + editable=False) + has_ipv6 = models.BooleanField("IPv6 capable", default=False, + editable=False) + active = models.BooleanField(default=True) + bandwidth = models.FloatField("bandwidth (mbits)", null=True, blank=True) + created = models.DateTimeField(editable=False) + + class Meta: + verbose_name = 'mirror URL' + def __unicode__(self): return self.url - class Meta: - verbose_name = 'Mirror URL' + + def address_families(self): + hostname = urlparse(self.url).hostname + info = socket.getaddrinfo(hostname, None, 0, socket.SOCK_STREAM) + families = [x[0] for x in info] + return families + + @property + def hostname(self): + return urlparse(self.url).hostname + + def clean(self): + try: + # Auto-map the protocol field by looking at the URL + protocol = urlparse(self.url).scheme + self.protocol = MirrorProtocol.objects.get(protocol=protocol) + except Exception as e: + raise ValidationError(e) + try: + families = self.address_families() + self.has_ipv4 = socket.AF_INET in families + self.has_ipv6 = socket.AF_INET6 in families + except socket.error: + # We don't fail in this case; we'll just set both to False + self.has_ipv4 = False + self.has_ipv6 = False + + def get_absolute_url(self): + return '/mirrors/%s/%d/' % (self.mirror.name, self.pk) + class MirrorRsync(models.Model): - ip = models.CharField(max_length=24) + # max length is 40 chars for full-form IPv6 addr + subnet + ip = IPNetworkField("IP") mirror = models.ForeignKey(Mirror, related_name="rsync_ips") + created = models.DateTimeField(editable=False) + def __unicode__(self): - return "%s" % (self.ip) + return unicode(self.ip) + + class Meta: + verbose_name = 'mirror rsync IP' + ordering = ('ip',) + + +class CheckLocation(models.Model): + hostname = models.CharField(max_length=255) + source_ip = models.GenericIPAddressField('source IP', + unpack_ipv4=True, unique=True) + country = CountryField() + created = models.DateTimeField(editable=False) + class Meta: - verbose_name = 'Mirror Rsync IP' + ordering = ('hostname', 'source_ip') + + def __unicode__(self): + return self.hostname + + @property + def family(self): + info = socket.getaddrinfo(self.source_ip, None, 0, 0, 0, + socket.AI_NUMERICHOST) + families = [x[0] for x in info] + return families[0] + + @property + def ip_version(self): + '''Returns integer '4' or '6'.''' + if self.family == socket.AF_INET6: + return 6 + if self.family == socket.AF_INET: + return 4 + return None + class MirrorLog(models.Model): url = models.ForeignKey(MirrorUrl, related_name="logs") + location = models.ForeignKey(CheckLocation, related_name="logs", null=True) check_time = models.DateTimeField(db_index=True) last_sync = models.DateTimeField(null=True) duration = models.FloatField(null=True) is_success = models.BooleanField(default=True) - error = models.CharField(max_length=255, blank=True, default='') + error = models.TextField(blank=True, default='') + + @property + def delay(self): + if self.last_sync is None: + return None + # sanity check, this shouldn't happen + if self.check_time < self.last_sync: + return timedelta() + return self.check_time - self.last_sync def __unicode__(self): - return "Check of %s at %s" % (url.url, check_time) + return "Check of %s at %s" % (self.url.url, self.check_time) class Meta: - verbose_name = 'Mirror Check Log' + verbose_name = 'mirror check log' + get_latest_by = 'check_time' + + +for model in (Mirror, MirrorProtocol, MirrorUrl, MirrorRsync, CheckLocation): + pre_save.connect(set_created_field, sender=model, + dispatch_uid="mirrors.models") # vim: set ts=4 sw=4 et: |