Improve ovs_lib bridge management

Add the ability to add and remove bridges, check for bridge
existence, and lookup the bridge associated with a port.

This change is in support of functional testing for an ovsdb
monitor.

Partial-Bug: #1177973

Change-Id: I419923d8d77983997cd347fcf063b0bc367c0bbc
This commit is contained in:
Maru Newby 2013-09-09 09:58:12 +00:00
parent acf0209b28
commit 010bd1f392
2 changed files with 141 additions and 17 deletions

View File

@ -44,10 +44,52 @@ class VifPort:
self.switch.br_name) self.switch.br_name)
class OVSBridge: class BaseOVS(object):
def __init__(self, br_name, root_helper):
self.br_name = br_name def __init__(self, root_helper):
self.root_helper = root_helper self.root_helper = root_helper
def run_vsctl(self, args, check_error=False):
full_args = ["ovs-vsctl", "--timeout=2"] + args
try:
return utils.execute(full_args, root_helper=self.root_helper)
except Exception as e:
LOG.error(_("Unable to execute %(cmd)s. Exception: %(exception)s"),
{'cmd': full_args, 'exception': e})
if check_error:
raise
def add_bridge(self, bridge_name):
self.run_vsctl(["--", "--may-exist", "add-br", bridge_name])
return OVSBridge(bridge_name, self.root_helper)
def delete_bridge(self, bridge_name):
self.run_vsctl(["--", "--if-exists", "del-br", bridge_name])
def bridge_exists(self, bridge_name):
try:
self.run_vsctl(['br-exists', bridge_name], check_error=True)
except RuntimeError as e:
if 'Exit code: 2\n' in str(e):
return False
raise
return True
def get_bridge_name_for_port_name(self, port_name):
try:
return self.run_vsctl(['port-to-br', port_name], check_error=True)
except RuntimeError as e:
if 'Exit code: 1\n' not in str(e):
raise
def port_exists(self, port_name):
return bool(self.get_bridge_name_for_port_name(port_name))
class OVSBridge(BaseOVS):
def __init__(self, br_name, root_helper):
super(OVSBridge, self).__init__(root_helper)
self.br_name = br_name
self.re_id = self.re_compile_id() self.re_id = self.re_compile_id()
self.defer_apply_flows = False self.defer_apply_flows = False
self.deferred_flows = {'add': '', 'mod': '', 'del': ''} self.deferred_flows = {'add': '', 'mod': '', 'del': ''}
@ -65,17 +107,15 @@ class OVSBridge:
'port': port}) 'port': port})
return re.compile(_re, re.M | re.X) return re.compile(_re, re.M | re.X)
def run_vsctl(self, args): def create(self):
full_args = ["ovs-vsctl", "--timeout=2"] + args self.add_bridge(self.br_name)
try:
return utils.execute(full_args, root_helper=self.root_helper) def destroy(self):
except Exception as e: self.delete_bridge(self.br_name)
LOG.error(_("Unable to execute %(cmd)s. Exception: %(exception)s"),
{'cmd': full_args, 'exception': e})
def reset_bridge(self): def reset_bridge(self):
self.run_vsctl(["--", "--if-exists", "del-br", self.br_name]) self.destroy()
self.run_vsctl(["add-br", self.br_name]) self.create()
def add_port(self, port_name): def add_port(self, port_name):
self.run_vsctl(["--", "--may-exist", "add-port", self.br_name, self.run_vsctl(["--", "--may-exist", "add-port", self.br_name,

View File

@ -26,6 +26,79 @@ from neutron.openstack.common import uuidutils
from neutron.tests import base from neutron.tests import base
class TestBaseOVS(base.BaseTestCase):
def setUp(self):
super(TestBaseOVS, self).setUp()
self.root_helper = 'sudo'
self.ovs = ovs_lib.BaseOVS(self.root_helper)
self.br_name = 'bridge1'
def test_add_bridge(self):
with mock.patch.object(self.ovs, 'run_vsctl') as mock_vsctl:
bridge = self.ovs.add_bridge(self.br_name)
mock_vsctl.assert_called_with(["--", "--may-exist",
"add-br", self.br_name])
self.assertEqual(bridge.br_name, self.br_name)
self.assertEqual(bridge.root_helper, self.ovs.root_helper)
def test_delete_bridge(self):
with mock.patch.object(self.ovs, 'run_vsctl') as mock_vsctl:
self.ovs.delete_bridge(self.br_name)
mock_vsctl.assert_called_with(["--", "--if-exists", "del-br",
self.br_name])
def test_bridge_exists_returns_true(self):
with mock.patch.object(self.ovs, 'run_vsctl') as mock_vsctl:
self.assertTrue(self.ovs.bridge_exists(self.br_name))
mock_vsctl.assert_called_with(['br-exists', self.br_name],
check_error=True)
def test_bridge_exists_returns_false_for_exit_code_2(self):
with mock.patch.object(self.ovs, 'run_vsctl',
side_effect=RuntimeError('Exit code: 2\n')):
self.assertFalse(self.ovs.bridge_exists('bridge1'))
def test_bridge_exists_raises_unknown_exception(self):
with mock.patch.object(self.ovs, 'run_vsctl',
side_effect=RuntimeError()):
with testtools.ExpectedException(RuntimeError):
self.ovs.bridge_exists('bridge1')
def test_get_bridge_name_for_port_name_returns_bridge_for_valid_port(self):
port_name = 'bar'
with mock.patch.object(self.ovs, 'run_vsctl',
return_value=self.br_name) as mock_vsctl:
bridge = self.ovs.get_bridge_name_for_port_name(port_name)
self.assertEqual(bridge, self.br_name)
mock_vsctl.assert_called_with(['port-to-br', port_name],
check_error=True)
def test_get_bridge_name_for_port_name_returns_none_for_exit_code_1(self):
with mock.patch.object(self.ovs, 'run_vsctl',
side_effect=RuntimeError('Exit code: 1\n')):
self.assertFalse(self.ovs.get_bridge_name_for_port_name('bridge1'))
def test_get_bridge_name_for_port_name_raises_unknown_exception(self):
with mock.patch.object(self.ovs, 'run_vsctl',
side_effect=RuntimeError()):
with testtools.ExpectedException(RuntimeError):
self.ovs.get_bridge_name_for_port_name('bridge1')
def _test_port_exists(self, br_name, result):
with mock.patch.object(self.ovs,
'get_bridge_name_for_port_name',
return_value=br_name):
self.assertEqual(self.ovs.port_exists('bar'), result)
def test_port_exists_returns_true_for_bridge_name(self):
self._test_port_exists(self.br_name, True)
def test_port_exists_returns_false_for_none(self):
self._test_port_exists(None, False)
class OVS_Lib_Test(base.BaseTestCase): class OVS_Lib_Test(base.BaseTestCase):
"""A test suite to excercise the OVS libraries shared by Neutron agents. """A test suite to excercise the OVS libraries shared by Neutron agents.
@ -66,12 +139,23 @@ class OVS_Lib_Test(base.BaseTestCase):
self.mox.VerifyAll() self.mox.VerifyAll()
def test_create(self):
self.br.add_bridge(self.BR_NAME)
self.mox.ReplayAll()
self.br.create()
self.mox.VerifyAll()
def test_destroy(self):
self.br.delete_bridge(self.BR_NAME)
self.mox.ReplayAll()
self.br.destroy()
self.mox.VerifyAll()
def test_reset_bridge(self): def test_reset_bridge(self):
utils.execute(["ovs-vsctl", self.TO, "--", self.br.destroy()
"--if-exists", "del-br", self.BR_NAME], self.br.create()
root_helper=self.root_helper)
utils.execute(["ovs-vsctl", self.TO, "add-br", self.BR_NAME],
root_helper=self.root_helper)
self.mox.ReplayAll() self.mox.ReplayAll()
self.br.reset_bridge() self.br.reset_bridge()