From 1e8a45f0dcf7293fb09979993285b0e6674201b0 Mon Sep 17 00:00:00 2001 From: Sylvain Afchain Date: Sat, 30 Nov 2013 00:49:02 +0100 Subject: [PATCH] L3 Agent can handle many external networks With this patch a l3 agent can handle any networks by setting the neutron parameter external_network_bridge and gateway_external_network_id to empty. Related-Bug: #1234750 Related-Bug: #1194350 Change-Id: Id260a239df23951da730513f40cda490002effc3 --- etc/l3_agent.ini | 12 +++-- neutron/agent/l3_agent.py | 10 +++- neutron/tests/unit/test_l3_agent.py | 71 +++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 6 deletions(-) diff --git a/etc/l3_agent.ini b/etc/l3_agent.ini index 777e10ad7f..c9913aaa62 100644 --- a/etc/l3_agent.ini +++ b/etc/l3_agent.ini @@ -27,10 +27,11 @@ # This is done by setting the specific router_id. # router_id = -# Each L3 agent can be associated with at most one external network. This -# value should be set to the UUID of that external network. If empty, -# the agent will enforce that only a single external networks exists and -# use that external network id +# When external_network_bridge is set, each L3 agent can be associated +# with no more than one external network. This value should be set to the UUID +# of that external network. To allow L3 agent support multiple external +# networks, both the external_network_bridge and gateway_external_network_id +# must be left empty. # gateway_external_network_id = # Indicates that this L3 agent should also handle routers that do not have @@ -40,7 +41,8 @@ # handle_internal_only_routers = True # Name of bridge used for external network traffic. This should be set to -# empty value for the linux bridge +# empty value for the linux bridge. when this parameter is set, each L3 agent +# can be associated with no more than one external network. # external_network_bridge = br-ex # TCP Port used by Neutron metadata server diff --git a/neutron/agent/l3_agent.py b/neutron/agent/l3_agent.py index 8853fb2c68..87fa481439 100644 --- a/neutron/agent/l3_agent.py +++ b/neutron/agent/l3_agent.py @@ -291,6 +291,13 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager): """Find UUID of single external network for this agent.""" if self.conf.gateway_external_network_id: return self.conf.gateway_external_network_id + + # L3 agent doesn't use external_network_bridge to handle external + # networks, so bridge_mappings with provider networks will be used + # and the L3 agent is able to handle any external networks. + if not self.conf.external_network_bridge: + return + try: return self.plugin_rpc.get_external_network_id(self.context) except rpc_common.RemoteError as e: @@ -686,7 +693,8 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, manager.Manager): ex_net_id = (r['external_gateway_info'] or {}).get('network_id') if not ex_net_id and not self.conf.handle_internal_only_routers: continue - if ex_net_id and ex_net_id != target_ex_net_id: + if (target_ex_net_id and ex_net_id and + ex_net_id != target_ex_net_id): continue cur_router_ids.add(r['id']) if r['id'] not in self.router_info: diff --git a/neutron/tests/unit/test_l3_agent.py b/neutron/tests/unit/test_l3_agent.py index fa48e71f04..17bd83da1b 100644 --- a/neutron/tests/unit/test_l3_agent.py +++ b/neutron/tests/unit/test_l3_agent.py @@ -26,6 +26,7 @@ from neutron.agent import l3_agent from neutron.agent.linux import interface from neutron.common import config as base_config from neutron.common import constants as l3_constants +from neutron.common import exceptions as n_exc from neutron.openstack.common import uuidutils from neutron.tests import base @@ -842,6 +843,76 @@ class TestBasicRouterOperations(base.BaseTestCase): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) self.assertEqual(['1234'], agent._router_ids()) + def test_process_routers_with_no_ext_net_in_conf(self): + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + self.plugin_api.get_external_network_id.return_value = 'aaa' + + routers = [ + {'id': _uuid(), + 'routes': [], + 'admin_state_up': True, + 'external_gateway_info': {'network_id': 'aaa'}}] + + agent._process_routers(routers) + self.assertIn(routers[0]['id'], agent.router_info) + + def test_process_routers_with_no_ext_net_in_conf_and_two_net_plugin(self): + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + + routers = [ + {'id': _uuid(), + 'routes': [], + 'admin_state_up': True, + 'external_gateway_info': {'network_id': 'aaa'}}] + + agent.router_info = {} + self.plugin_api.get_external_network_id.side_effect = ( + n_exc.TooManyExternalNetworks()) + self.assertRaises(n_exc.TooManyExternalNetworks, + agent._process_routers, + routers) + self.assertNotIn(routers[0]['id'], agent.router_info) + + def test_process_routers_with_ext_net_in_conf(self): + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + self.plugin_api.get_external_network_id.return_value = 'aaa' + + routers = [ + {'id': _uuid(), + 'routes': [], + 'admin_state_up': True, + 'external_gateway_info': {'network_id': 'aaa'}}, + {'id': _uuid(), + 'routes': [], + 'admin_state_up': True, + 'external_gateway_info': {'network_id': 'bbb'}}] + + agent.router_info = {} + self.conf.set_override('gateway_external_network_id', 'aaa') + agent._process_routers(routers) + self.assertIn(routers[0]['id'], agent.router_info) + self.assertNotIn(routers[1]['id'], agent.router_info) + + def test_process_routers_with_no_bridge_no_ext_net_in_conf(self): + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + self.plugin_api.get_external_network_id.return_value = 'aaa' + + routers = [ + {'id': _uuid(), + 'routes': [], + 'admin_state_up': True, + 'external_gateway_info': {'network_id': 'aaa'}}, + {'id': _uuid(), + 'routes': [], + 'admin_state_up': True, + 'external_gateway_info': {'network_id': 'bbb'}}] + + agent.router_info = {} + self.conf.set_override('external_network_bridge', '') + agent._process_routers(routers) + self.assertIn(routers[0]['id'], agent.router_info) + self.assertIn(routers[1]['id'], agent.router_info) + def test_nonexistent_interface_driver(self): self.conf.set_override('interface_driver', None) with mock.patch.object(l3_agent, 'LOG') as log: