models.py 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. # -*- coding: utf-8 -*-
  2. from django.db import models
  3. from django.core.exceptions import ValidationError
  4. from netfields import CidrAddressField, NetManager
  5. from netaddr import IPNetwork, IPSet
  6. class IPPool(models.Model):
  7. """Pool of IP addresses (either v4 or v6)."""
  8. name = models.CharField(max_length=255, blank=False, null=False,
  9. verbose_name='Name of the IP pool')
  10. description = models.TextField(blank=True, null=False,
  11. verbose_name='Description of the IP pool')
  12. inet = CidrAddressField()
  13. objects = NetManager()
  14. def __unicode__(self):
  15. return self.name
  16. class IPSubnet(models.Model):
  17. inet = CidrAddressField(blank=True, verbose_name="Leave empty for automatic allocation")
  18. objects = NetManager()
  19. ip_pool = models.ForeignKey(IPPool)
  20. offer_subscription = models.ForeignKey('offers.OfferSubscription',
  21. related_name='ip_subnet')
  22. def clean(self):
  23. # TODO: define default subnet sizes for IPv4 / IPv6. Or better,
  24. # integrate it in the pool definition.
  25. if not self.inet:
  26. # Automatically allocate a free subnet
  27. pool = IPSet([self.ip_pool.inet])
  28. used = IPSet((s.inet for s in self.ip_pool.ipsubnet_set.all()))
  29. free = pool.difference(used)
  30. SUBNET_SIZE = 32 if self.ip_pool.inet.version == 4 else 56
  31. # Generator for efficiency (we don't build the whole list)
  32. available = (p for p in free.iter_cidrs() if p.prefixlen <= SUBNET_SIZE)
  33. # TODO: for IPv4, get rid of the network and broadcast
  34. # addresses? Not really needed nowadays, and we usually don't
  35. # have a real subnet in practice (i.e. Ethernet segment), but
  36. # many /32.
  37. try:
  38. first_free = available.next()
  39. except StopIteration:
  40. raise ValidationError('Unable to allocate an IP subnet in the specified pool: not enough space left.')
  41. self.inet = first_free.subnet(SUBNET_SIZE, 1).next()
  42. else:
  43. # TODO:
  44. # Check that we are included in the IP pool.
  45. # Check that we don't conflict with existing subnets
  46. pass
  47. def __unicode__(self):
  48. return str(self.inet)