Browse Source

Refactor subnet code for clarity

Baptiste Jonglez 11 years ago
parent
commit
1487c61b37
1 changed files with 29 additions and 23 deletions
  1. 29 23
      coin/resources/models.py

+ 29 - 23
coin/resources/models.py

@@ -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)