From 43ece8fb4c1bf77b662d28fa3bff42342b2a7a41 Mon Sep 17 00:00:00 2001 From: Gary Kotton Date: Tue, 18 Dec 2012 12:20:50 +0000 Subject: [PATCH] Add OVS cleanup utility Fixes bug 1091605 The utility should be called after rebooting an appliance. This will purge the openvswitch of configured tap devices. A configuration variable quantum_ports has been added. This is by default True which indicates that only Quantum ports will be deleted from the OVS. If this is set as False then all ports on the bridge will be deleted. Change-Id: I442f64cf82f95bfa99d7765eb09db1ce2ecf602e --- bin/quantum-ovs-cleanup | 26 +++++++ quantum/agent/linux/ovs_lib.py | 18 +++++ quantum/agent/ovs_cleanup_util.py | 76 +++++++++++++++++++ .../tests/unit/openvswitch/test_ovs_lib.py | 34 +++++++++ quantum/tests/unit/test_agent_ovs_cleanup.py | 42 ++++++++++ setup.py | 1 + 6 files changed, 197 insertions(+) create mode 100755 bin/quantum-ovs-cleanup create mode 100644 quantum/agent/ovs_cleanup_util.py create mode 100644 quantum/tests/unit/test_agent_ovs_cleanup.py diff --git a/bin/quantum-ovs-cleanup b/bin/quantum-ovs-cleanup new file mode 100755 index 00000000000..096b8a73d0e --- /dev/null +++ b/bin/quantum-ovs-cleanup @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2012 Openstack, LLC. +# 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 os +import sys +sys.path.insert(0, os.getcwd()) + +from quantum.agent.ovs_cleanup_util import main + + +main() diff --git a/quantum/agent/linux/ovs_lib.py b/quantum/agent/linux/ovs_lib.py index 9eaa67e30f0..ec4194d3a2f 100644 --- a/quantum/agent/linux/ovs_lib.py +++ b/quantum/agent/linux/ovs_lib.py @@ -268,6 +268,15 @@ class OVSBridge: LOG.info("Unable to parse regex results. Exception: %s", e) return + def delete_ports(self, all_ports=False): + if all_ports: + port_names = self.get_port_name_list() + else: + port_names = (port.port_name for port in self.get_vif_ports()) + + for port_name in port_names: + self.delete_port(port_name) + def get_bridge_for_iface(root_helper, iface): args = ["ovs-vsctl", "--timeout=2", "iface-to-br", iface] @@ -276,3 +285,12 @@ def get_bridge_for_iface(root_helper, iface): except Exception, e: LOG.error(_("iface %s not found. Exception: %s"), iface, e) return None + + +def get_bridges(root_helper): + args = ["ovs-vsctl", "--timeout=2", "list-br"] + try: + return utils.execute(args, root_helper=root_helper).strip().split("\n") + except Exception, e: + LOG.error(_("Unable to retrieve bridges. Exception: %s"), e) + return [] diff --git a/quantum/agent/ovs_cleanup_util.py b/quantum/agent/ovs_cleanup_util.py new file mode 100644 index 00000000000..ac4f7ee5bb7 --- /dev/null +++ b/quantum/agent/ovs_cleanup_util.py @@ -0,0 +1,76 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2012 OpenStack LLC. +# 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 sys + +from quantum.agent import l3_agent +from quantum.agent.linux import interface +from quantum.agent.linux import ovs_lib +from quantum.common import config +from quantum.openstack.common import cfg +from quantum.openstack.common import log as logging + + +LOG = logging.getLogger(__name__) + + +def setup_conf(): + """Setup the cfg for the clean up utility. + + Use separate setup_conf for the utility because there are many options + from the main config that do not apply during clean-up. + """ + opts = [ + cfg.BoolOpt('ovs_all_ports', + default=False, + help='True to delete all ports on all the OpenvSwitch ' + 'bridges. False to delete ports created by Quantum ' + 'on integration and external network bridges.') + ] + + conf = cfg.CommonConfigOpts() + conf.register_opts(opts) + conf.register_opts(l3_agent.L3NATAgent.OPTS) + conf.register_opts(interface.OPTS) + config.setup_logging(conf) + return conf + + +def main(): + """Main method for cleaning up OVS bridges. + + The utility cleans up the integration bridges used by Quantum. + """ + + conf = setup_conf() + conf(sys.argv) + + configuration_bridges = set([conf.ovs_integration_bridge, + conf.external_network_bridge]) + ovs_bridges = set(ovs_lib.get_bridges(conf.root_helper)) + + if conf.ovs_all_ports: + bridges = ovs_bridges + else: + bridges = configuration_bridges & ovs_bridges + + for bridge in bridges: + LOG.info(_("Cleaning %s"), bridge) + ovs = ovs_lib.OVSBridge(bridge, conf.root_helper) + ovs.delete_ports(all_ports=conf.ovs_all_ports) + + LOG.info(_("OVS cleanup completed successfully")) diff --git a/quantum/tests/unit/openvswitch/test_ovs_lib.py b/quantum/tests/unit/openvswitch/test_ovs_lib.py index e67eb9ba265..8fbfabde090 100644 --- a/quantum/tests/unit/openvswitch/test_ovs_lib.py +++ b/quantum/tests/unit/openvswitch/test_ovs_lib.py @@ -314,3 +314,37 @@ class OVS_Lib_Test(unittest.TestCase): self.mox.ReplayAll() self.assertIsNone(ovs_lib.get_bridge_for_iface(root_helper, iface)) self.mox.VerifyAll() + + def test_delete_all_ports(self): + self.mox.StubOutWithMock(self.br, 'get_port_name_list') + self.br.get_port_name_list().AndReturn(['port1']) + self.mox.StubOutWithMock(self.br, 'delete_port') + self.br.delete_port('port1') + self.mox.ReplayAll() + self.br.delete_ports(all_ports=True) + self.mox.VerifyAll() + + def test_delete_quantum_ports(self): + port1 = ovs_lib.VifPort('tap1234', 1, str(uuid.uuid4()), + 'ca:fe:de:ad:be:ef', 'br') + port2 = ovs_lib.VifPort('tap5678', 2, str(uuid.uuid4()), + 'ca:ee:de:ad:be:ef', 'br') + ports = [port1, port2] + self.mox.StubOutWithMock(self.br, 'get_vif_ports') + self.br.get_vif_ports().AndReturn([port1, port2]) + self.mox.StubOutWithMock(self.br, 'delete_port') + self.br.delete_port('tap1234') + self.br.delete_port('tap5678') + self.mox.ReplayAll() + self.br.delete_ports(all_ports=False) + self.mox.VerifyAll() + + def test_get_bridges(self): + bridges = ['br-int', 'br-ex'] + root_helper = 'sudo' + utils.execute(["ovs-vsctl", self.TO, "list-br"], + root_helper=root_helper).AndReturn('br-int\nbr-ex\n') + + self.mox.ReplayAll() + self.assertEqual(ovs_lib.get_bridges(root_helper), bridges) + self.mox.VerifyAll() diff --git a/quantum/tests/unit/test_agent_ovs_cleanup.py b/quantum/tests/unit/test_agent_ovs_cleanup.py new file mode 100644 index 00000000000..d8e105260b5 --- /dev/null +++ b/quantum/tests/unit/test_agent_ovs_cleanup.py @@ -0,0 +1,42 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2012 OpenStack LLC. +# 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 +import unittest2 as unittest + +from quantum.agent.linux import ovs_lib +from quantum.agent import ovs_cleanup_util as util + + +class TestOVSCleanup(unittest.TestCase): + def test_setup_conf(self): + with mock.patch('quantum.common.config.setup_logging'): + conf = util.setup_conf() + self.assertEqual(conf.external_network_bridge, 'br-ex') + self.assertEqual(conf.ovs_integration_bridge, 'br-int') + self.assertFalse(conf.ovs_all_ports) + + def test_main(self): + with mock.patch('quantum.common.config.setup_logging'): + br_patch = mock.patch('quantum.agent.linux.ovs_lib.get_bridges') + with br_patch as mock_get_bridges: + mock_get_bridges.return_value = ['br-int', 'br-ex'] + with mock.patch( + 'quantum.agent.linux.ovs_lib.OVSBridge') as ovs: + util.main() + ovs.assert_has_calls([mock.call().delete_ports( + all_ports=False)]) diff --git a/setup.py b/setup.py index ba63a32e4a1..1791ee8ea87 100644 --- a/setup.py +++ b/setup.py @@ -124,6 +124,7 @@ setuptools.setup( 'quantum.plugins.nec.agent.nec_quantum_agent:main', 'quantum-server = quantum.server:main', 'quantum-debug = quantum.debug.shell:main', + 'quantum-ovs-cleanup = quantum.agent.ovs_cleanup_util:main', ] }, )