enable router deletion logic in l3-agent

bug 1039387

related to bp quantum-l3-fwd-nat

previous diff logic did not attempt to detect that a router was deleted.

Also, enabled actual deletion of namespace, since 066e48be08f155c29887f5846902fbf5e41b8c89 fixed the issue that was causing that to break.

Also, make router loop pay attention to admin_state_up on router ports.

Change-Id: Id76736dd3207472dfc282097e5b27740e05aaa3a
This commit is contained in:
Dan Wendlandt 2012-08-21 10:26:18 -07:00
parent d15151d42a
commit 961adae44f
2 changed files with 72 additions and 43 deletions

View File

@ -89,6 +89,7 @@ class L3NATAgent(object):
def __init__(self, conf):
self.conf = conf
self.router_info = {}
if not conf.interface_driver:
LOG.error(_('You must specify an interface driver'))
@ -120,36 +121,35 @@ class L3NATAgent(object):
linux_utils.execute(['sysctl', '-w', 'net.ipv4.ip_forward=0'],
self.conf.root_helper, check_exit_code=False)
self._destroy_router_namespaces()
self._destroy_all_router_namespaces()
# enable forwarding
linux_utils.execute(['sysctl', '-w', 'net.ipv4.ip_forward=1'],
self.conf.root_helper, check_exit_code=False)
def _destroy_router_namespaces(self):
def _destroy_all_router_namespaces(self):
"""Destroy all router namespaces on the host to eliminate
all stale linux devices, iptables rules, and namespaces.
"""
root_ip = ip_lib.IPWrapper(self.conf.root_helper)
for ns in root_ip.get_namespaces(self.conf.root_helper):
if ns.startswith(NS_PREFIX):
ns_ip = ip_lib.IPWrapper(self.conf.root_helper,
namespace=ns)
for d in ns_ip.get_devices():
if d.name.startswith(INTERNAL_DEV_PREFIX):
# device is on default bridge
self.driver.unplug(d.name)
elif d.name.startswith(EXTERNAL_DEV_PREFIX):
self.driver.unplug(d.name,
bridge=
self.conf.external_network_bridge)
try:
self._destroy_router_namespace(ns)
except:
LOG.exception("couldn't delete namespace '%s'" % ns)
# FIXME(danwent): disabling actual deletion of namespace
# until we figure out why it fails. Having deleted all
# devices, the only harm here should be the clutter of
# the namespace lying around.
# ns_ip.netns.delete(ns)
def _destroy_router_namespace(self, namespace):
ns_ip = ip_lib.IPWrapper(self.conf.root_helper,
namespace=namespace)
for d in ns_ip.get_devices():
if d.name.startswith(INTERNAL_DEV_PREFIX):
# device is on default bridge
self.driver.unplug(d.name)
elif d.name.startswith(EXTERNAL_DEV_PREFIX):
self.driver.unplug(d.name,
bridge=self.conf.external_network_bridge)
ns_ip.netns.delete(namespace)
def daemon_loop(self):
@ -159,22 +159,34 @@ class L3NATAgent(object):
# update notifications.
# Likewise, it does not handle removing routers
self.router_info = {}
while True:
try:
#TODO(danwent): provide way to limit this to a single
# router, for model where agent runs in dedicated VM
for r in self.qclient.list_routers()['routers']:
if r['id'] not in self.router_info:
self.router_info[r['id']] = (RouterInfo(r['id'],
self.conf.root_helper))
ri = self.router_info[r['id']]
self.process_router(ri)
self.do_single_loop()
except:
LOG.exception("Error running l3_nat daemon_loop")
time.sleep(self.polling_interval)
def do_single_loop(self):
prev_router_ids = set(self.router_info)
cur_router_ids = set()
# identify and update new or modified routers
for r in self.qclient.list_routers()['routers']:
cur_router_ids.add(r['id'])
if r['id'] not in self.router_info:
self.router_info[r['id']] = RouterInfo(r['id'],
self.conf.root_helper)
ri = self.router_info[r['id']]
self.process_router(ri)
# identify and remove routers that no longer exist
for router_id in prev_router_ids - cur_router_ids:
ri = self.router_info[router_id]
del self.router_info[router_id]
self._destroy_router_namespace(ri.ns_name())
prev_router_ids = cur_router_ids
def _set_subnet_info(self, port):
ips = port['fixed_ips']
if not ips:
@ -195,21 +207,24 @@ class L3NATAgent(object):
device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF)['ports']
existing_port_ids = set([p['id'] for p in ri.internal_ports])
current_port_ids = set([p['id'] for p in internal_ports])
current_port_ids = set([p['id'] for p in internal_ports
if p['admin_state_up']])
new_ports = [p for p in internal_ports if
p['id'] in current_port_ids and
p['id'] not in existing_port_ids]
old_ports = [p for p in ri.internal_ports if
p['id'] not in current_port_ids]
for p in internal_ports:
if p['id'] not in existing_port_ids:
self._set_subnet_info(p)
ri.internal_ports.append(p)
self.internal_network_added(ri, ex_gw_port, p['id'],
p['ip_cidr'], p['mac_address'])
for p in new_ports:
self._set_subnet_info(p)
ri.internal_ports.append(p)
self.internal_network_added(ri, ex_gw_port, p['id'],
p['ip_cidr'], p['mac_address'])
port_ids_to_remove = existing_port_ids - current_port_ids
for p in ri.internal_ports:
if p['id'] in port_ids_to_remove:
ri.internal_ports.remove(p)
self.internal_network_removed(ri, ex_gw_port, p['id'],
p['ip_cidr'])
for p in old_ports:
ri.internal_ports.remove(p)
self.internal_network_removed(ri, ex_gw_port, p['id'],
p['ip_cidr'])
internal_cidrs = [p['ip_cidr'] for p in ri.internal_ports]

View File

@ -189,6 +189,7 @@ class TestBasicRouterOperations(unittest.TestCase):
'fixed_ips': [{'ip_address': '19.4.4.4',
'subnet_id': _uuid()}]}
internal_port = {'id': _uuid(),
'admin_state_up': True,
'fixed_ips': [{'ip_address': '35.4.4.4',
'subnet_id': _uuid()}],
'mac_address': 'ca:fe:de:ad:be:ef'}
@ -221,6 +222,21 @@ class TestBasicRouterOperations(unittest.TestCase):
self.client_inst.list_ports.return_value = {'ports': []}
agent.process_router(ri)
def testSingleLoopRouterRemoval(self):
agent = l3_agent.L3NATAgent(self.conf)
self.client_inst.list_ports.return_value = {'ports': []}
self.client_inst.list_routers.return_value = {'routers': [
{'id': _uuid()}]}
agent.do_single_loop()
self.client_inst.list_routers.return_value = {'routers': []}
agent.do_single_loop()
# verify that remove is called
self.assertEquals(self.mock_ip.get_devices.call_count, 1)
def testDaemonLoop(self):
# just take a pass through the loop, then raise on time.sleep()
@ -231,8 +247,6 @@ class TestBasicRouterOperations(unittest.TestCase):
pass
time_sleep.side_effect = ExpectedException()
self.client_inst.list_routers.return_value = {'routers':
[{'id': _uuid()}]}
agent = l3_agent.L3NATAgent(self.conf)
self.assertRaises(ExpectedException, agent.daemon_loop)
@ -250,7 +264,7 @@ class TestBasicRouterOperations(unittest.TestCase):
FakeDev('qgw-aaaa')]
agent = l3_agent.L3NATAgent(self.conf)
agent._destroy_router_namespaces()
agent._destroy_all_router_namespaces()
def testMain(self):
agent_mock_p = mock.patch('quantum.agent.l3_agent.L3NATAgent')