From: Jenkins Date: Thu, 21 Aug 2014 21:28:00 +0000 (+0000) Subject: Merge "Ensure ip6tables are used only if ipv6 is enabled in kernel" X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=5f5eb7aee71982c54829d3b1a9a456c7d6aba993;p=openstack-build%2Fneutron-build.git Merge "Ensure ip6tables are used only if ipv6 is enabled in kernel" --- 5f5eb7aee71982c54829d3b1a9a456c7d6aba993 diff --cc neutron/agent/l3_agent.py index d94ffe6d2,84976fb69..f20fee878 --- a/neutron/agent/l3_agent.py +++ b/neutron/agent/l3_agent.py @@@ -143,102 -143,10 +144,103 @@@ class L3PluginApi(n_rpc.RpcProxy) version='1.3') +class LinkLocalAddressPair(netaddr.IPNetwork): + def __init__(self, addr): + super(LinkLocalAddressPair, self).__init__(addr) + + def get_pair(self): + """Builds an address pair from the first and last addresses. """ + return (netaddr.IPNetwork("%s/%s" % (self.network, self.prefixlen)), + netaddr.IPNetwork("%s/%s" % (self.broadcast, self.prefixlen))) + + +class LinkLocalAllocator(object): + """Manages allocation of link local IP addresses. + + These link local addresses are used for routing inside the fip namespaces. + The associations need to persist across agent restarts to maintain + consistency. Without this, there is disruption in network connectivity + as the agent rewires the connections with the new IP address assocations. + + Persisting these in the database is unnecessary and would degrade + performance. + """ + def __init__(self, state_file, subnet): + """Read the file with previous allocations recorded. + + See the note in the allocate method for more detail. + """ + self.state_file = state_file + subnet = netaddr.IPNetwork(subnet) + + self.allocations = {} + + self.remembered = {} + for line in self._read(): + key, cidr = line.strip().split(',') + self.remembered[key] = LinkLocalAddressPair(cidr) + + self.pool = set(LinkLocalAddressPair(s) for s in subnet.subnet(31)) + self.pool.difference_update(self.remembered.values()) + + def allocate(self, key): + """Try to allocate a link local address pair. + + I expect this to work in all cases because I expect the pool size to be + large enough for any situation. Nonetheless, there is some defensive + programming in here. + + Since the allocations are persisted, there is the chance to leak + allocations which should have been released but were not. This leak + could eventually exhaust the pool. + + So, if a new allocation is needed, the code first checks to see if + there are any remembered allocations for the key. If not, it checks + the free pool. If the free pool is empty then it dumps the remembered + allocations to free the pool. This final desparate step will not + happen often in practice. + """ + if key in self.remembered: + self.allocations[key] = self.remembered.pop(key) + return self.allocations[key] + + if not self.pool: + # Desparate times. Try to get more in the pool. + self.pool.update(self.remembered.values()) + self.remembered.clear() + if not self.pool: + # More than 256 routers on a compute node! + raise RuntimeError(_("Cannot allocate link local address")) + + self.allocations[key] = self.pool.pop() + self._write_allocations() + return self.allocations[key] + + def release(self, key): + self.pool.add(self.allocations.pop(key)) + self._write_allocations() + + def _write_allocations(self): + current = ["%s,%s\n" % (k, v) for k, v in self.allocations.items()] + remembered = ["%s,%s\n" % (k, v) for k, v in self.remembered.items()] + current.extend(remembered) + self._write(current) + + def _write(self, lines): + with open(self.state_file, "w") as f: + f.writelines(lines) + + def _read(self): + if not os.path.exists(self.state_file): + return [] + with open(self.state_file) as f: + return f.readlines() + + class RouterInfo(object): - def __init__(self, router_id, root_helper, use_namespaces, router): + def __init__(self, router_id, root_helper, use_namespaces, router, + use_ipv6=False): self.router_id = router_id self.ex_gw_port = None self._snat_enabled = None