evorepo/mirrors/models.py
Dan McGee 122273496a Make delay a property, not a function
And re-indent the URL details log template now that we've broken it out.

Signed-off-by: Dan McGee <dan@archlinux.org>
2014-11-08 14:04:31 -06:00

190 lines
6.1 KiB
Python

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
class Mirror(models.Model):
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, 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("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 = ('name',)
def __unicode__(self):
return self.name
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:
ordering = ('protocol',)
class MirrorUrl(models.Model):
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
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):
# 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 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:
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.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" % (self.url.url, self.check_time)
class Meta:
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: