From: Gary Kotton Date: Tue, 18 Dec 2012 12:20:50 +0000 (+0000) Subject: Add OVS cleanup utility X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=767859ddb947f281c4ada930ccc21cc6f0ba6806;p=openstack-build%2Fneutron-build.git Add OVS cleanup utility Fixes bug 1091605 The utility should be called after rebooting an appliance. This will purge the openvswicth 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 --- diff --git a/bin/quantum-ovs-cleanup b/bin/quantum-ovs-cleanup new file mode 100755 index 000000000..096b8a73d --- /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 00f6586b0..6e19602fa 100644 --- a/quantum/agent/linux/ovs_lib.py +++ b/quantum/agent/linux/ovs_lib.py @@ -271,6 +271,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] @@ -279,3 +288,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 000000000..46bb3de7f --- /dev/null +++ b/quantum/agent/ovs_cleanup_util.py @@ -0,0 +1,75 @@ +# 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 deletes all ports on the bridge. False deletes ' + 'those created by Quantum.'), + ] + + 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 52d8fbc73..d84780da9 100644 --- a/quantum/tests/unit/openvswitch/test_ovs_lib.py +++ b/quantum/tests/unit/openvswitch/test_ovs_lib.py @@ -313,3 +313,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, uuidutils.generate_uuid(), + 'ca:fe:de:ad:be:ef', 'br') + port2 = ovs_lib.VifPort('tap5678', 2, uuidutils.generate_uuid(), + '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 000000000..77a60cc69 --- /dev/null +++ b/quantum/tests/unit/test_agent_ovs_cleanup.py @@ -0,0 +1,43 @@ +# 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 +from quantum.openstack.common import uuidutils + + +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 dbcd40593..d9c23547c 100644 --- a/setup.py +++ b/setup.py @@ -139,6 +139,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', ] }, )