--- /dev/null
+# 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()
: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)
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, .*
--- /dev/null
+# 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)
"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.
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