]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add neutron-linuxbridge-cleanup util
authorMathieu Gagné <mgagne@iweb.com>
Tue, 8 Sep 2015 21:07:07 +0000 (17:07 -0400)
committerCedric Brandily <zzelle@gmail.com>
Thu, 1 Oct 2015 19:54:34 +0000 (21:54 +0200)
Removal of empty bridges have been disabled [1] to fix a race condition
between Nova and Neutron where a bridge would be removed if
the only instance using it is rebooted. This means empty bridges
will pile up over time.

This script can be used to periodically remove empty bridges by running it
on compute nodes.

Note: Usage of this script can still trigger the original race condition.
It should be used when you don't expect anyone do be doing operations
on their instances.

[1] Commit 8dd8a7d93564168b98fa2350eedf56acede42b0f

DocImpact: Add neutron-linuxbridge-cleanup util
Related-bug: #1328546
Closes-bug: #1497027
Co-Authored-By: Cedric Brandily <zzelle@gmail.com>
Change-Id: Ieb2796381579ad295abf361ce483d979a53d2bd6

neutron/cmd/linuxbridge_cleanup.py [new file with mode: 0644]
neutron/tests/common/net_helpers.py
neutron/tests/contrib/functional-testing.filters
neutron/tests/functional/cmd/test_linuxbridge_cleanup.py [new file with mode: 0644]
neutron/tests/tools.py
setup.cfg

diff --git a/neutron/cmd/linuxbridge_cleanup.py b/neutron/cmd/linuxbridge_cleanup.py
new file mode 100644 (file)
index 0000000..3ecb315
--- /dev/null
@@ -0,0 +1,76 @@
+#    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 sys
+
+from oslo_config import cfg
+from oslo_log import log as logging
+
+from neutron.common import config
+from neutron.common import utils as n_utils
+from neutron.i18n import _LE, _LI
+from neutron.plugins.ml2.drivers.linuxbridge.agent \
+    import linuxbridge_neutron_agent
+
+
+LOG = logging.getLogger(__name__)
+
+
+def remove_empty_bridges():
+    try:
+        interface_mappings = n_utils.parse_mappings(
+            cfg.CONF.LINUX_BRIDGE.physical_interface_mappings)
+    except ValueError as e:
+        LOG.error(_LE("Parsing physical_interface_mappings failed: %s."), e)
+        sys.exit(1)
+    LOG.info(_LI("Interface mappings: %s."), interface_mappings)
+
+    try:
+        bridge_mappings = n_utils.parse_mappings(
+            cfg.CONF.LINUX_BRIDGE.bridge_mappings)
+    except ValueError as e:
+        LOG.error(_LE("Parsing bridge_mappings failed: %s."), e)
+        sys.exit(1)
+    LOG.info(_LI("Bridge mappings: %s."), bridge_mappings)
+
+    lb_manager = linuxbridge_neutron_agent.LinuxBridgeManager(
+        bridge_mappings, interface_mappings)
+
+    # NOTE(mgagne) Don't remove pre-existing user-defined bridges
+    bridge_names = set(lb_manager.get_all_neutron_bridges())
+    bridge_names -= set(bridge_mappings.values())
+
+    for bridge_name in bridge_names:
+        if lb_manager.get_tap_devices_count(bridge_name):
+            continue
+
+        try:
+            lb_manager.delete_bridge(bridge_name)
+            LOG.info(_LI("Linux bridge %s deleted"), bridge_name)
+        except RuntimeError:
+            LOG.exception(_LE("Linux bridge %s delete failed"), bridge_name)
+    LOG.info(_LI("Linux bridge cleanup completed successfully"))
+
+
+def main():
+    """Main method for cleaning up empty linux bridges.
+
+    This tool deletes every empty linux bridge managed by linuxbridge agent
+    (brq.* linux bridges) except thes ones defined using bridge_mappings option
+    in section LINUX_BRIDGE (created by deployers).
+
+    This tool should not be called during an instance create, migrate, etc. as
+    it can delete a linux bridge about to be used by nova.
+    """
+    cfg.CONF(sys.argv[1:])
+    config.setup_logging()
+    remove_empty_bridges()
index a79c1ff20b0f4d92dcc8cc1c76ad7729a24ba257..143345dc6bb4c0470d12c78be570ea15cfc06cfa 100644 (file)
@@ -518,10 +518,14 @@ class LinuxBridgeFixture(fixtures.Fixture):
     :type namespace: str
     """
 
+    def __init__(self, prefix=BR_PREFIX):
+        super(LinuxBridgeFixture, self).__init__()
+        self.prefix = prefix
+
     def _setUp(self):
         self.namespace = self.useFixture(NamespaceFixture()).name
         self.bridge = common_base.create_resource(
-            BR_PREFIX,
+            self.prefix,
             bridge_lib.BridgeDevice.addbr,
             namespace=self.namespace)
         self.addCleanup(self.bridge.delbr)
index c0c7b18ea766cdbf48467c47529cd5fac9086586..eb6169e97435cebe277dad73f5f5d07f79a51e2e 100644 (file)
@@ -18,3 +18,6 @@ nc_kill: KillFilter, root, nc, -9
 ncbsd_kill: KillFilter, root, nc.openbsd, -9
 ncat_kill: KillFilter, root, ncat, -9
 ss_filter: CommandFilter, ss, root
+
+# enable neutron-linuxbridge-cleanup from namespace
+lb_cleanup_filter: RegExpFilter, neutron-linuxbridge-cleanup, root, neutron-linuxbridge-cleanup, --config-file, .*
diff --git a/neutron/tests/functional/cmd/test_linuxbridge_cleanup.py b/neutron/tests/functional/cmd/test_linuxbridge_cleanup.py
new file mode 100644 (file)
index 0000000..a3c13dd
--- /dev/null
@@ -0,0 +1,89 @@
+# Copyright (c) 2015 Thales Services SAS
+# 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 fixtures
+import mock
+
+from neutron.agent.linux import ip_lib
+from neutron.common import constants
+from neutron.plugins.ml2.drivers.linuxbridge.agent import \
+    linuxbridge_neutron_agent as lb_agent
+from neutron.tests.common import config_fixtures
+from neutron.tests.common import net_helpers
+from neutron.tests.functional import base
+from neutron.tests import tools
+
+
+class LinuxbridgeCleanupTest(base.BaseSudoTestCase):
+
+    def _test_linuxbridge_cleanup(self, bridge_exists, callback):
+        br_fixture = self.useFixture(
+            tools.SafeCleanupFixture(
+                net_helpers.LinuxBridgeFixture(
+                    prefix=lb_agent.BRIDGE_NAME_PREFIX))).fixture
+
+        config = callback(br_fixture)
+
+        temp_dir = self.useFixture(fixtures.TempDir()).path
+        conf = self.useFixture(config_fixtures.ConfigFileFixture(
+            base_filename='neutron.conf',
+            config=config,
+            temp_dir=temp_dir))
+
+        cmd = 'neutron-linuxbridge-cleanup', '--config-file', conf.filename
+        ip_wrapper = ip_lib.IPWrapper(br_fixture.namespace)
+        ip_wrapper.netns.execute(cmd)
+
+        self.assertEqual(bridge_exists, ip_lib.device_exists(
+            br_fixture.bridge.name, br_fixture.namespace))
+
+    def test_cleanup_empty_bridge(self):
+
+        def callback(br_fixture):
+            return config_fixtures.ConfigDict()
+
+        self._test_linuxbridge_cleanup(False, callback)
+
+    def test_no_cleanup_bridge_with_tap(self):
+
+        def callback(br_fixture):
+            # TODO(cbrandily): refactor net_helpers to avoid mocking it
+            mock.patch.object(
+                net_helpers, 'VETH0_PREFIX',
+                new_callable=mock.PropertyMock(
+                    return_value=constants.TAP_DEVICE_PREFIX + '0')).start()
+            mock.patch.object(
+                net_helpers, 'VETH1_PREFIX',
+                new_callable=mock.PropertyMock(
+                    return_value=constants.TAP_DEVICE_PREFIX + '1')).start()
+
+            self.useFixture(
+                tools.SafeCleanupFixture(
+                    net_helpers.LinuxBridgePortFixture(
+                        br_fixture.bridge, br_fixture.namespace)))
+            return config_fixtures.ConfigDict()
+
+        self._test_linuxbridge_cleanup(True, callback)
+
+    def test_no_cleanup_bridge_in_bridge_mappings(self):
+
+        def callback(br_fixture):
+            br_name = br_fixture.bridge.name
+            conf = config_fixtures.ConfigDict()
+            conf.update(
+                {'LINUX_BRIDGE': {'bridge_mappings': 'physnet:%s' % br_name}})
+            return conf
+
+        self._test_linuxbridge_cleanup(True, callback)
index 63f730f8c213db699ad7dfd8e7c78cbfe5b7f964..f3af67e0b1771f0f1ff137e86fe49bccc930754b 100644 (file)
@@ -65,6 +65,24 @@ class WarningsFixture(fixtures.Fixture):
                 "always", category=wtype, module='^neutron\\.')
 
 
+class SafeCleanupFixture(fixtures.Fixture):
+    """Catch errors in daughter fixture cleanup."""
+
+    def __init__(self, fixture):
+        self.fixture = fixture
+
+    def _setUp(self):
+
+        def cleanUp():
+            try:
+                self.fixture.cleanUp()
+            except Exception:
+                pass
+
+        self.fixture.setUp()
+        self.addCleanup(cleanUp)
+
+
 """setup_mock_calls and verify_mock_calls are convenient methods
 to setup a sequence of mock calls.
 
index 49486dfcf686770cffe69b339fe10a258496509b..4884742331992e813e8d893e7011b30825176a5a 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -82,6 +82,7 @@ console_scripts =
     neutron-ipset-cleanup = neutron.cmd.ipset_cleanup:main
     neutron-l3-agent = neutron.cmd.eventlet.agents.l3:main
     neutron-linuxbridge-agent = neutron.plugins.ml2.drivers.linuxbridge.agent.linuxbridge_neutron_agent:main
+    neutron-linuxbridge-cleanup = neutron.cmd.linuxbridge_cleanup:main
     neutron-metadata-agent = neutron.cmd.eventlet.agents.metadata:main
     neutron-mlnx-agent = neutron.cmd.eventlet.plugins.mlnx_neutron_agent:main
     neutron-netns-cleanup = neutron.cmd.netns_cleanup:main