|
@@ -56,32 +56,38 @@ class IPSubnet(models.Model):
|
|
|
blank=True,
|
|
|
verbose_name="nameserver to use for the delegation of reverse DNS")
|
|
|
|
|
|
+ def allocate(self):
|
|
|
+ """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(self.ip_pool.default_subnetsize, 1).next()
|
|
|
+
|
|
|
+ def validate_inclusion(self):
|
|
|
+ """Check that we are included in the IP pool"""
|
|
|
+ if not self.inet in self.ip_pool.inet:
|
|
|
+ raise ValidationError('Subnet must be included in the IP pool.')
|
|
|
+ # Check that we don't conflict with existing subnets.
|
|
|
+ conflicting = self.ip_pool.ipsubnet_set.filter(Q(inet__net_contained_or_equal=self.inet) |
|
|
|
+ Q(inet__net_contains_or_equals=self.inet)).exclude(id=self.id)
|
|
|
+ if conflicting:
|
|
|
+ raise ValidationError('Subnet must not intersect with existing subnets.\nIntersected subnets: {}.'.format(conflicting))
|
|
|
+
|
|
|
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(self.ip_pool.default_subnetsize, 1).next()
|
|
|
+ self.allocate()
|
|
|
else:
|
|
|
- # Check that we are included in the IP pool.
|
|
|
- if not self.inet in self.ip_pool.inet:
|
|
|
- raise ValidationError('Subnet must be included in the IP pool.')
|
|
|
- # Check that we don't conflict with existing subnets.
|
|
|
- conflicting = self.ip_pool.ipsubnet_set.filter(Q(inet__net_contained_or_equal=self.inet) |
|
|
|
- Q(inet__net_contains_or_equals=self.inet)).exclude(id=self.id)
|
|
|
- if conflicting:
|
|
|
- raise ValidationError('Subnet must not intersect with existing subnets.\nIntersected subnets: {}.'.format(conflicting))
|
|
|
+ self.validate_inclusion()
|
|
|
|
|
|
def __unicode__(self):
|
|
|
return str(self.inet)
|