# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models from polymorphic import PolymorphicModel from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse from netfields import InetAddressField, NetManager from coin.configuration.models import Configuration # from coin.offers.backends import ValidateBackendType from coin import validation """BIG FAT WARNING Ce code requiert une sévère factorisation avec vpn/models.py et housing/models.py """ FINGERPRINT_TYPES = ( ('ED25519', 'ED25519'), ('RSA', 'RSA'), ('ECDSA', 'ECDSA') ) PROTOCOLE_TYPES = ( ('VNC', 'VNC'), ) class VPSConfiguration(Configuration): url_namespace = "vps" activated = models.BooleanField(default=False, verbose_name='activé') ipv4_endpoint = InetAddressField(validators=[validation.validate_v4], verbose_name="IPv4", blank=True, null=True, help_text="Adresse IPv4 utilisée par " "défaut sur le VPS") ipv6_endpoint = InetAddressField(validators=[validation.validate_v6], verbose_name="IPv6", blank=True, null=True, help_text="Adresse IPv6 utilisée par " "défaut sur le VPS") objects = NetManager() def get_absolute_url(self): return reverse('vps:details', args=[str(self.pk)]) # This method is part of the general configuration interface. def subnet_event(self): self.check_endpoints(delete=True) # We potentially changed the endpoints, so we need to save. Also, # saving will update the subnets in the LDAP backend. self.full_clean() self.save() def get_subnets(self, version): subnets = self.ip_subnet.all() return [subnet for subnet in subnets if subnet.inet.version == version] def generate_endpoints(self, v4=True, v6=True): """Generate IP endpoints in one of the attributed IP subnets. If there is no available subnet for a given address family, then no endpoint is generated for this address family. If there already is an endpoint, do nothing. Returns True if an endpoint was generated. TODO: this should be factored for other technologies (DSL, etc) """ subnets = self.ip_subnet.all() updated = False if v4 and self.ipv4_endpoint is None: subnets_v4 = [s for s in subnets if s.inet.version == 4] if len(subnets_v4) > 0: self.ipv4_endpoint = subnets_v4[0].inet.ip updated = True if v6 and self.ipv6_endpoint is None: subnets_v6 = [s for s in subnets if s.inet.version == 6] if len(subnets_v6) > 0: # With v6, we choose the second host of the subnet (cafe::1) gen = subnets_v6[0].inet.iter_hosts() gen.next() self.ipv6_endpoint = gen.next() updated = True return updated def check_endpoints(self, delete=False): """Check that the IP endpoints are included in one of the attributed IP subnets. If [delete] is True, then simply delete the faulty endpoints instead of raising an exception. """ error = "L'IP {} n'est pas dans un réseau attribué." subnets = self.ip_subnet.all() is_faulty = lambda endpoint : endpoint and not any([endpoint in subnet.inet for subnet in subnets]) if is_faulty(self.ipv4_endpoint): if delete: self.ipv4_endpoint = None else: raise ValidationError(error.format(self.ipv4_endpoint)) if is_faulty(self.ipv6_endpoint): if delete: self.ipv6_endpoint = None else: raise ValidationError(error.format(self.ipv6_endpoint)) def clean(self): # If saving for the first time and IP endpoints are not specified, # generate them automatically. if self.pk is None: self.generate_endpoints() self.check_endpoints() def __unicode__(self): return 'VPS ' + str(self.offersubscription.member.username) + ' ' + self.offersubscription.member.last_name class Meta: verbose_name = 'VPS' class FingerPrint(PolymorphicModel): vps = models.ForeignKey(VPSConfiguration, verbose_name="vps") algo = models.CharField(max_length=256, verbose_name="algo", choices=FINGERPRINT_TYPES) fingerprint = models.CharField(max_length=256, verbose_name="empreinte") length = models.IntegerField(verbose_name="longueur de la clé", null=True) class Meta: verbose_name = 'Empreinte' class Console(models.Model): vps = models.OneToOneField(VPSConfiguration, verbose_name="vps") protocol = models.CharField(max_length=256, verbose_name="protocole", choices=PROTOCOLE_TYPES) domain = models.CharField(max_length=256, verbose_name="nom de domaine", blank=True, null=True) port = models.IntegerField(verbose_name="port", null=True) password_link = models.URLField(verbose_name="Mot de passe", blank=True, null=True, help_text="Lien à usage unique (détruit après ouverture)") class Meta: verbose_name = 'Console'