allocate subnets and networks automatically
authorScott Baker <smbaker@gmail.com>
Sat, 27 Jul 2013 01:15:42 +0000 (18:15 -0700)
committerScott Baker <smbaker@gmail.com>
Sat, 27 Jul 2013 01:15:42 +0000 (18:15 -0700)
planetstack/core/models/network.py

index 1ea7115..8819179 100644 (file)
@@ -11,17 +11,37 @@ SUBNET_BASE = "10.0.0.0"
 SUBNET_NODE_BITS = 12     # enough for 4096 bits per subnet
 SUBNET_SUBNET_BITS = 12   # enough for 4096 private networks
 
+def ip_to_int(ip):
+    return int(socket.inet_aton(ip).encode('hex'),16)
+
+def int_to_ip(i):
+    return socket.inet_ntoa(hex(i)[2:].zfill(8).decode('hex'))
+
 def find_unused_subnet(base, subnet_bits, node_bits, existing_subnets):
-    # start at the first allocatable subnet
+    # enumerate possible subnets until we find one that isn't used
     i=1
     while True:
-        subnet_i = int(socket.inet_aton(base).encode('hex'),16) | (i<<node_bits)
-        subnet = socket.inet_ntoa(hex(subnet_i)[2:].zfill(8).decode('hex')) + "/" + str(32-node_bits)
+        subnet_i = ip_to_int(base) | (i<<node_bits)
+        subnet = int_to_ip(subnet_i) + "/" + str(32-node_bits)
         if (subnet not in existing_subnets):
             return subnet
         i=i+1
         # TODO: we could run out...
 
+def find_unused_address(subnet, existingAddresses):
+    # enumerate possible addresses until we find one that isn't used
+    (network, bits) = subnet.split("/")
+    network_i = ip_to_int(network)
+    max_addr = 1<<(32-int(bits))
+    i = 1
+    while True:
+        if i>=max_addr:
+            raise ValueError("No more ips available")
+        ip = int_to_ip(network_i | i)
+        if not (ip in existingAddresses):
+            return ip
+        i=i+1
+
 class NetworkTemplate(PlCoreBase):
     VISIBILITY_CHOICES = (('public', 'public'), ('private', 'private'))
 
@@ -41,7 +61,7 @@ class Network(PlCoreBase):
 
     guaranteedBandwidth = models.IntegerField(default=0)
     permittedSlices = models.ManyToManyField(Slice, blank=True, related_name="permittedNetworks")
-    boundSlivers = models.ManyToManyField(Sliver, blank=True, related_name="boundNetworks", through="NetworkSliver")
+    slivers = models.ManyToManyField(Sliver, blank=True, related_name="boundNetworks", through="NetworkSliver")
 
     def __unicode__(self):  return u'%s' % (self.name)
 
@@ -49,6 +69,10 @@ class Network(PlCoreBase):
         existingSubnets = [x.subnet for x in Network.objects.all()]
         return find_unused_subnet(SUBNET_BASE, SUBNET_SUBNET_BITS, SUBNET_NODE_BITS, existingSubnets)
 
+    def allocateAddress(self):
+        existingAddresses = [x.ip for x in self.networksliver_set.all()]
+        return find_unused_address(self.subnet, existingAddresses)
+
     def save(self, *args, **kwds):
         if not self.subnet:
             self.subnet = self.allocateSubnet()
@@ -59,6 +83,11 @@ class NetworkSliver(PlCoreBase):
     sliver = models.ForeignKey(Sliver)
     ip = models.GenericIPAddressField(help_text="Sliver ip address", blank=True, null=True)
 
+    def save(self, *args, **kwds):
+        if not self.ip:
+            self.ip = self.network.allocateAddress()
+        super(NetworkSliver, self).save(*args, **kwds)
+
     def __unicode__(self):  return u'foo!'
 
 class Router(PlCoreBase):