From a8d1b7218038025d066472cfb9c17037f31bf791 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Wed, 29 Jul 2015 16:32:43 -0700 Subject: [PATCH] Check that VXLAN is not in use in LB VXLAN check The Linux bridge VXLAN supported check was only checking that the test interface didn't exist instead of checking that both the interface and the VXLAN didn't exist. This caused it to fail on startup if a VXLAN interface existed under a different name using one of the VXLANs that the agent tried to test support with. This patch adds a check to ensure that the VXLAN ID isn't in use as well. Closes-Bug: #1470579 Change-Id: I3a91ce54da86e319b7a4485dfae3fc99885383d4 --- neutron/agent/linux/ip_lib.py | 8 +++++++ .../agent/linuxbridge_neutron_agent.py | 24 +++++++++++++++---- .../functional/agent/linux/test_ip_lib.py | 12 ++++++++++ .../agent/test_linuxbridge_neutron_agent.py | 1 + 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/neutron/agent/linux/ip_lib.py b/neutron/agent/linux/ip_lib.py index e3268b7aa..cadbd019f 100644 --- a/neutron/agent/linux/ip_lib.py +++ b/neutron/agent/linux/ip_lib.py @@ -723,6 +723,14 @@ class IpNetnsCommand(IpCommandBase): return False +def vxlan_in_use(segmentation_id, namespace=None): + """Return True if VXLAN VNID is in use by an interface, else False.""" + ip_wrapper = IPWrapper(namespace=namespace) + interfaces = ip_wrapper.netns.execute(["ip", "-d", "link", "list"], + check_exit_code=True) + return 'vxlan id %s ' % segmentation_id in interfaces + + def device_exists(device_name, namespace=None): """Return True if the device exists in the namespace.""" try: 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 b9747a4ec..861736ef7 100644 --- a/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/linuxbridge/agent/linuxbridge_neutron_agent.py @@ -31,6 +31,7 @@ from oslo_log import log as logging import oslo_messaging from oslo_service import loopingcall from oslo_service import service +from oslo_utils import excutils from six import moves from neutron.agent.linux import bridge_lib @@ -248,7 +249,19 @@ class LinuxBridgeManager(object): args['tos'] = cfg.CONF.VXLAN.tos if cfg.CONF.VXLAN.l2_population: args['proxy'] = True - int_vxlan = self.ip.add_vxlan(interface, segmentation_id, **args) + try: + int_vxlan = self.ip.add_vxlan(interface, segmentation_id, + **args) + except RuntimeError: + with excutils.save_and_reraise_exception() as ctxt: + # perform this check after an attempt rather than before + # to avoid excessive lookups and a possible race condition. + if ip_lib.vxlan_in_use(segmentation_id): + ctxt.reraise = False + LOG.error(_LE("Unable to create VXLAN interface for " + "VNI %s because it is in use by another " + "interface."), segmentation_id) + return None int_vxlan.link.set_up() LOG.debug("Done creating vxlan interface %s", interface) return interface @@ -526,10 +539,11 @@ class LinuxBridgeManager(object): test_iface = None for seg_id in moves.range(1, p_const.MAX_VXLAN_VNI + 1): - if not ip_lib.device_exists( - self.get_vxlan_device_name(seg_id)): - test_iface = self.ensure_vxlan(seg_id) - break + if (ip_lib.device_exists(self.get_vxlan_device_name(seg_id)) + or ip_lib.vxlan_in_use(seg_id)): + continue + test_iface = self.ensure_vxlan(seg_id) + break else: LOG.error(_LE('No valid Segmentation ID to perform UCAST test.')) return False diff --git a/neutron/tests/functional/agent/linux/test_ip_lib.py b/neutron/tests/functional/agent/linux/test_ip_lib.py index 8804599ec..4695a964e 100644 --- a/neutron/tests/functional/agent/linux/test_ip_lib.py +++ b/neutron/tests/functional/agent/linux/test_ip_lib.py @@ -101,6 +101,18 @@ class IpLibTestCase(IpLibTestFramework): self.assertFalse( ip_lib.device_exists(attr.name, namespace=attr.namespace)) + def test_vxlan_exists(self): + attr = self.generate_device_details() + ip = ip_lib.IPWrapper(namespace=attr.namespace) + ip.netns.add(attr.namespace) + self.addCleanup(ip.netns.delete, attr.namespace) + self.assertFalse(ip_lib.vxlan_in_use(9999, namespace=attr.namespace)) + device = ip.add_vxlan(attr.name, 9999) + self.addCleanup(self._safe_delete_device, device) + self.assertTrue(ip_lib.vxlan_in_use(9999, namespace=attr.namespace)) + device.link.delete() + self.assertFalse(ip_lib.vxlan_in_use(9999, namespace=attr.namespace)) + def test_ipwrapper_get_device_by_ip(self): attr = self.generate_device_details() self.manage_device(attr) 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 55b9bd821..3f227a9ff 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 @@ -873,6 +873,7 @@ class TestLinuxBridgeManager(base.BaseTestCase): self, expected, l2_population, iproute_arg_supported, fdb_append): cfg.CONF.set_override('l2_population', l2_population, 'VXLAN') with mock.patch.object(ip_lib, 'device_exists', return_value=False),\ + mock.patch.object(ip_lib, 'vxlan_in_use', return_value=False),\ mock.patch.object(self.lbm, 'delete_vxlan', return_value=None),\ -- 2.45.2