123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 |
- # -*- coding: utf-8 -*-
- from django.db import models
- from django.core.exceptions import ValidationError
- from django.core.validators import MaxValueValidator
- from netfields import CidrAddressField, NetManager
- from netaddr import IPNetwork, IPSet
- class IPPool(models.Model):
- """Pool of IP addresses (either v4 or v6)."""
- name = models.CharField(max_length=255, blank=False, null=False,
- verbose_name='Name of the IP pool')
- default_subnetsize = models.PositiveSmallIntegerField(blank=False,
- verbose_name='Default subnet size to allocate to subscribers in this pool',
- validators=[MaxValueValidator(64)])
- inet = CidrAddressField()
- objects = NetManager()
- def clean_default_subnetsize(self):
- if self.inet.version == 4:
- if not self.inet.prefixlen <= self.default_subnetsize <= 32:
- raise ValidationError('Invalid default subnet size')
- elif self.inet.version == 6:
- if not self.inet.prefixlen <= self.default_subnetsize <= 64:
- raise ValidationError('Invalid default subnet size')
- else:
- pass
- def __unicode__(self):
- return self.name
- class IPSubnet(models.Model):
- inet = CidrAddressField(blank=True, verbose_name="Leave empty for automatic allocation")
- objects = NetManager()
- ip_pool = models.ForeignKey(IPPool)
- offer_subscription = models.ForeignKey('offers.OfferSubscription',
- related_name='ip_subnet')
- def clean(self):
- if not self.inet:
- # Automatically allocate a free subnet
- pool = IPSet([self.ip_pool.inet])
- used = IPSet((s.inet for s in self.ip_pool.ipsubnet_set.all()))
- free = pool.difference(used)
- # Generator for efficiency (we don't build the whole list)
- available = (p for p in free.iter_cidrs() if p.prefixlen <= self.ip_pool.default_subnetsize)
- # TODO: for IPv4, get rid of the network and broadcast
- # addresses? Not really needed nowadays, and we usually don't
- # have a real subnet in practice (i.e. Ethernet segment), but
- # many /32.
- try:
- first_free = available.next()
- except StopIteration:
- raise ValidationError('Unable to allocate an IP subnet in the specified pool: not enough space left.')
- self.inet = first_free.subnet(SUBNET_SIZE, 1).next()
- else:
- # TODO:
- # Check that we are included in the IP pool.
- # Check that we don't conflict with existing subnets
- pass
- def __unicode__(self):
- return str(self.inet)
|