From 58e388261313025d1e713d4c17c99db12404b75f Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Wed, 29 Jul 2015 17:28:50 -0700 Subject: [PATCH] Validate interface_mappings on Linux bridge init Verify that the interfaces actually exist that are defined in interface_mappings on Linux bridge startup. If they do not, exit immediately similar to how OVS handles incorrect bridge_mappings. This prevents an unfriendly exception in the rpc setup routine. Change-Id: I050b9b66aa0b27f148e67123eedf29fe332e6f65 Closes-Bug: #1470584 --- .../agent/linuxbridge_neutron_agent.py | 9 ++++ .../functional/agent/linux/test_ip_lib.py | 5 +- .../functional/agent/test_l2_lb_agent.py | 47 +++++++++++++++++++ .../agent/test_linuxbridge_neutron_agent.py | 12 +++-- 4 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 neutron/tests/functional/agent/test_l2_lb_agent.py diff --git a/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py b/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py index b9747a4ec04..4d5c596db13 100644 --- a/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py @@ -77,6 +77,7 @@ class NetworkSegment(object): class LinuxBridgeManager(object): def __init__(self, interface_mappings): self.interface_mappings = interface_mappings + self.validate_interface_mappings() self.ip = ip_lib.IPWrapper() # VXLAN related parameters: self.local_ip = cfg.CONF.VXLAN.local_ip @@ -93,6 +94,14 @@ class LinuxBridgeManager(object): # Store network mapping to segments self.network_map = {} + def validate_interface_mappings(self): + for physnet, interface in self.interface_mappings.items(): + if not ip_lib.device_exists(interface): + LOG.error(_LE("Interface %(intf)s for physical network %(net)s" + " does not exist. Agent terminated!"), + {'intf': interface, 'net': physnet}) + sys.exit(1) + def interface_exists_on_bridge(self, bridge, interface): directory = '/sys/class/net/%s/brif' % bridge for filename in os.listdir(directory): diff --git a/neutron/tests/functional/agent/linux/test_ip_lib.py b/neutron/tests/functional/agent/linux/test_ip_lib.py index 8804599ec5b..dce36a47fca 100644 --- a/neutron/tests/functional/agent/linux/test_ip_lib.py +++ b/neutron/tests/functional/agent/linux/test_ip_lib.py @@ -73,8 +73,9 @@ class IpLibTestFramework(functional_base.BaseSudoTestCase): :return: A tuntap ip_lib.IPDevice """ ip = ip_lib.IPWrapper(namespace=attr.namespace) - ip.netns.add(attr.namespace) - self.addCleanup(ip.netns.delete, attr.namespace) + if attr.namespace: + ip.netns.add(attr.namespace) + self.addCleanup(ip.netns.delete, attr.namespace) tap_device = ip.add_tuntap(attr.name) self.addCleanup(self._safe_delete_device, tap_device) tap_device.link.set_address(attr.mac_address) diff --git a/neutron/tests/functional/agent/test_l2_lb_agent.py b/neutron/tests/functional/agent/test_l2_lb_agent.py new file mode 100644 index 00000000000..32ea6c5f9f4 --- /dev/null +++ b/neutron/tests/functional/agent/test_l2_lb_agent.py @@ -0,0 +1,47 @@ +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +from oslo_log import log as logging +import testtools + +from neutron.plugins.ml2.drivers.linuxbridge.agent import \ + linuxbridge_neutron_agent +from neutron.tests.functional.agent.linux import test_ip_lib + +LOG = logging.getLogger(__name__) +lba = linuxbridge_neutron_agent + + +class LinuxBridgeAgentTests(test_ip_lib.IpLibTestFramework): + + def setUp(self): + super(LinuxBridgeAgentTests, self).setUp() + agent_rpc = ('neutron.agent.rpc.PluginApi') + mock.patch(agent_rpc).start() + mock.patch('neutron.agent.rpc.PluginReportStateAPI').start() + + def test_validate_interface_mappings(self): + mappings = {'physnet1': 'int1', 'physnet2': 'int2'} + with testtools.ExpectedException(SystemExit): + lba.LinuxBridgeManager(mappings) + self.manage_device( + self.generate_device_details()._replace(namespace=None, + name='int1')) + with testtools.ExpectedException(SystemExit): + lba.LinuxBridgeManager(mappings) + self.manage_device( + self.generate_device_details()._replace(namespace=None, + name='int2')) + lba.LinuxBridgeManager(mappings) diff --git a/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py b/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py index 55b9bd821b4..5673958958b 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_linuxbridge_neutron_agent.py @@ -50,7 +50,9 @@ class TestLinuxBridge(base.BaseTestCase): interface_mappings = {'physnet1': 'eth1'} with mock.patch.object(ip_lib.IPWrapper, - 'get_device_by_ip', return_value=None): + 'get_device_by_ip', return_value=None),\ + mock.patch.object(ip_lib, 'device_exists', + return_value=True): self.linux_bridge = linuxbridge_neutron_agent.LinuxBridgeManager( interface_mappings) @@ -355,7 +357,9 @@ class TestLinuxBridgeManager(base.BaseTestCase): self.interface_mappings = {'physnet1': 'eth1'} with mock.patch.object(ip_lib.IPWrapper, - 'get_device_by_ip', return_value=None): + 'get_device_by_ip', return_value=None),\ + mock.patch.object(ip_lib, 'device_exists', + return_value=True): self.lbm = linuxbridge_neutron_agent.LinuxBridgeManager( self.interface_mappings) @@ -935,7 +939,9 @@ class TestLinuxBridgeRpcCallbacks(base.BaseTestCase): self.agent_id = 1 with mock.patch.object( ip_lib.IPWrapper, - 'get_device_by_ip', return_value=None): + 'get_device_by_ip', return_value=None),\ + mock.patch.object(ip_lib, 'device_exists', + return_value=True): self.br_mgr = (linuxbridge_neutron_agent. LinuxBridgeManager({'physnet1': 'eth1'}))