Browse Source

Add automatic prefix allocation

Baptiste Jonglez 11 years ago
parent
commit
717b112c04
1 changed files with 29 additions and 2 deletions
  1. 29 2
      coin/resources/models.py

+ 29 - 2
coin/resources/models.py

@@ -1,7 +1,8 @@
 # -*- coding: utf-8 -*-
 from django.db import models
+from django.core.exceptions import ValidationError
 from netfields import CidrAddressField, NetManager
-
+from netaddr import IPNetwork, IPSet
 
 class IPPool(models.Model):
     """Pool of IP addresses (either v4 or v6)."""
@@ -17,11 +18,37 @@ class IPPool(models.Model):
 
 
 class IPSubnet(models.Model):
-    inet = CidrAddressField()
+    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):
+        # TODO: define default subnet sizes for IPv4 / IPv6. Or better,
+        # integrate it in the pool definition.
+        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)
+            SUBNET_SIZE = 32 if self.ip_pool.inet.version == 4 else 56
+            # Generator for efficiency (we don't build the whole list)
+            available = (p for p in free.iter_cidrs() if p.prefixlen <= SUBNET_SIZE)
+            # 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)