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
This commit is contained in:
Gary Kotton 2012-12-18 12:20:50 +00:00
parent ca8ed5eee6
commit 767859ddb9
6 changed files with 197 additions and 0 deletions

26
bin/quantum-ovs-cleanup Executable file
View File

@ -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()

View File

@ -271,6 +271,15 @@ class OVSBridge:
LOG.info(_("Unable to parse regex results. Exception: %s"), e) LOG.info(_("Unable to parse regex results. Exception: %s"), e)
return 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): def get_bridge_for_iface(root_helper, iface):
args = ["ovs-vsctl", "--timeout=2", "iface-to-br", 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: except Exception, e:
LOG.error(_("iface %s not found. Exception: %s"), iface, e) LOG.error(_("iface %s not found. Exception: %s"), iface, e)
return None 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 []

View File

@ -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"))

View File

@ -313,3 +313,37 @@ class OVS_Lib_Test(unittest.TestCase):
self.mox.ReplayAll() self.mox.ReplayAll()
self.assertIsNone(ovs_lib.get_bridge_for_iface(root_helper, iface)) self.assertIsNone(ovs_lib.get_bridge_for_iface(root_helper, iface))
self.mox.VerifyAll() 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()

View File

@ -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)])

View File

@ -139,6 +139,7 @@ setuptools.setup(
'quantum.plugins.nec.agent.nec_quantum_agent:main', 'quantum.plugins.nec.agent.nec_quantum_agent:main',
'quantum-server = quantum.server:main', 'quantum-server = quantum.server:main',
'quantum-debug = quantum.debug.shell:main', 'quantum-debug = quantum.debug.shell:main',
'quantum-ovs-cleanup = quantum.agent.ovs_cleanup_util:main',
] ]
}, },
) )