Merge branch 'stable-2.9' into stable-2.10
[ganeti-local] / lib / network.py
index 170a8b7..8059e31 100644 (file)
@@ -29,8 +29,20 @@ from bitarray import bitarray
 
 from ganeti import errors
 
+
+def _ComputeIpv4NumHosts(network_size):
+  """Derives the number of hosts in an IPv4 network from the size.
+
+  """
+  return 2 ** (32 - network_size)
+
+
 IPV4_NETWORK_MIN_SIZE = 30
-IPV4_NETWORK_MIN_NUM_HOSTS = 2 ** (32 - IPV4_NETWORK_MIN_SIZE)
+# FIXME: This limit is for performance reasons. Remove when refactoring
+# for performance tuning was successful.
+IPV4_NETWORK_MAX_SIZE = 16
+IPV4_NETWORK_MIN_NUM_HOSTS = _ComputeIpv4NumHosts(IPV4_NETWORK_MIN_SIZE)
+IPV4_NETWORK_MAX_NUM_HOSTS = _ComputeIpv4NumHosts(IPV4_NETWORK_MAX_SIZE)
 
 
 class AddressPool(object):
@@ -58,6 +70,13 @@ class AddressPool(object):
     self.net = network
 
     self.network = ipaddr.IPNetwork(self.net.network)
+    if self.network.numhosts > IPV4_NETWORK_MAX_NUM_HOSTS:
+      raise errors.AddressPoolError("A big network with %s host(s) is currently"
+                                    " not supported. please specify at most a"
+                                    " /%s network" %
+                                    (str(self.network.numhosts),
+                                     IPV4_NETWORK_MAX_SIZE))
+
     if self.network.numhosts < IPV4_NETWORK_MIN_NUM_HOSTS:
       raise errors.AddressPoolError("A network with only %s host(s) is too"
                                     " small, please specify at least a /%s"
@@ -134,14 +153,12 @@ class AddressPool(object):
   def Validate(self):
     assert len(self.reservations) == self._GetSize()
     assert len(self.ext_reservations) == self._GetSize()
-    all_res = self.reservations & self.ext_reservations
-    assert not all_res.any()
 
     if self.gateway is not None:
       assert self.gateway in self.network
 
     if self.network6 and self.gateway6:
-      assert self.gateway6 in self.network6
+      assert self.gateway6 in self.network6 or self.gateway6.is_link_local
 
     return True
 
@@ -169,25 +186,40 @@ class AddressPool(object):
     """
     return self.all_reservations.to01().replace("1", "X").replace("0", ".")
 
-  def IsReserved(self, address):
+  def IsReserved(self, address, external=False):
     """Checks if the given IP is reserved.
 
     """
     idx = self._GetAddrIndex(address)
-    return self.all_reservations[idx]
+    if external:
+      return self.ext_reservations[idx]
+    else:
+      return self.reservations[idx]
 
   def Reserve(self, address, external=False):
     """Mark an address as used.
 
     """
-    if self.IsReserved(address):
-      raise errors.AddressPoolError("%s is already reserved" % address)
+    if self.IsReserved(address, external):
+      if external:
+        msg = "IP %s is already externally reserved" % address
+      else:
+        msg = "IP %s is already used by an instance" % address
+      raise errors.AddressPoolError(msg)
+
     self._Mark(address, external=external)
 
   def Release(self, address, external=False):
     """Release a given address reservation.
 
     """
+    if not self.IsReserved(address, external):
+      if external:
+        msg = "IP %s is not externally reserved" % address
+      else:
+        msg = "IP %s is not used by an instance" % address
+      raise errors.AddressPoolError(msg)
+
     self._Mark(address, value=False, external=external)
 
   def GetFreeAddress(self):