From 4f2f5eeb8bbed7385cafbcd8ba2acb39870f4941 Mon Sep 17 00:00:00 2001 From: yong sheng gong Date: Tue, 31 Oct 2017 00:05:20 +0800 Subject: [PATCH] Delete ovs port if namespace is corrupted This patch isolates a corrupted dhcp namespace and protects the ovs from being blasted by thousands of dangling ovs ports created by dhcp agent. Change-Id: I80138b3c37f41a18dc488a306768b8e2fa299eda Closes-bug: #1728642 --- neutron/agent/linux/interface.py | 18 ++++++++-- .../tests/unit/agent/linux/test_interface.py | 34 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/neutron/agent/linux/interface.py b/neutron/agent/linux/interface.py index 269706acf77..e454ca9f45a 100644 --- a/neutron/agent/linux/interface.py +++ b/neutron/agent/linux/interface.py @@ -19,6 +19,7 @@ import time import netaddr from neutron_lib import constants from oslo_log import log as logging +from oslo_utils import excutils import six from neutron.agent.common import ovs_lib @@ -377,8 +378,21 @@ class OVSInterfaceDriver(LinuxInterfaceDriver): # Add an interface created by ovs to the namespace. if not self.conf.ovs_use_veth and namespace: - namespace_obj = ip.ensure_namespace(namespace) - namespace_obj.add_device_to_namespace(ns_dev) + try: + namespace_obj = ip.ensure_namespace(namespace) + namespace_obj.add_device_to_namespace(ns_dev) + except exceptions.ProcessExecutionError: + # To prevent the namespace failure from blasting + # ovs, the ovs port created should be reverted + # When the namespace is corrupted, the ProcessExecutionError + # has execption message as: + # Exit code: 2; Stdin: ; Stdout: ; Stderr: RTNETLINK + # answers: Invalid argument + LOG.warning("Failed to plug interface %s into bridge %s, " + "cleaning up", device_name, bridge) + with excutils.save_and_reraise_exception(): + ovs = ovs_lib.OVSBridge(bridge) + ovs.delete_port(tap_name) # NOTE(ihrachys): the order here is significant: we must set MTU after # the device is moved into a namespace, otherwise OVS bridge does not diff --git a/neutron/tests/unit/agent/linux/test_interface.py b/neutron/tests/unit/agent/linux/test_interface.py index 95bff8da863..fba54d8e919 100644 --- a/neutron/tests/unit/agent/linux/test_interface.py +++ b/neutron/tests/unit/agent/linux/test_interface.py @@ -15,10 +15,12 @@ import mock from neutron_lib import constants +from oslo_utils import excutils from neutron.agent.common import ovs_lib from neutron.agent.linux import interface from neutron.agent.linux import ip_lib +from neutron.common import exceptions from neutron.conf.agent import common as config from neutron.tests import base @@ -457,6 +459,32 @@ class TestOVSInterfaceDriver(TestBase): self.ip.assert_has_calls(expected) + def test_plug_new(self): + with mock.patch('neutron.agent.ovsdb.impl_idl._connection'): + bridge = 'br-int' + namespace = '01234567-1234-1234-99' + with mock.patch.object(ovs_lib.OVSBridge, + 'delete_port') as delete_port: + with mock.patch.object(ovs_lib.OVSBridge, 'replace_port'): + ovs = interface.OVSInterfaceDriver(self.conf) + reraise = mock.patch.object( + excutils, 'save_and_reraise_exception') + reraise.start() + proEr = exceptions.ProcessExecutionError('', 2) + processExecutionError = mock.Mock(side_effect=proEr) + ip = self.ip.return_value + ip.ensure_namespace.side_effect = processExecutionError + ovs.plug_new( + '01234567-1234-1234-99', + 'port-1234', + 'tap0', + 'aa:bb:cc:dd:ee:ff', + bridge=bridge, + namespace=namespace, + prefix='veth', + mtu=9000) + delete_port.assert_called_once_with('tap0') + def test_unplug(self, bridge=None): if not bridge: bridge = 'br-int' @@ -533,6 +561,12 @@ class TestOVSInterfaceDriverWithVeth(TestOVSInterfaceDriver): root_dev.assert_has_calls([mock.call.link.set_up()]) ns_dev.assert_has_calls([mock.call.link.set_up()]) + def test_plug_new(self, bridge=None, namespace=None): + # The purpose of test_plug_new in parent class(TestOVSInterfaceDriver) + # is to test exception(exceptions.ProcessExecutionError), method here + # would not go through that code, So just pass + pass + def test_unplug(self, bridge=None): if not bridge: bridge = 'br-int'