models.py 2.7 KB

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