Merge "OVSBridge: use ovs-ofctl with at least OF protocol version x"

This commit is contained in:
Jenkins 2017-09-05 18:44:31 +00:00 committed by Gerrit Code Review
commit 287d5ff26b
2 changed files with 78 additions and 5 deletions

View File

@ -190,12 +190,31 @@ class BaseOVS(object):
return {k: _cfg.get(k, OVS_DEFAULT_CAPS[k]) for k in OVS_DEFAULT_CAPS}
# Map from version string to on-the-wire protocol version encoding:
OF_PROTOCOL_TO_VERSION = {
constants.OPENFLOW10: 1,
constants.OPENFLOW11: 2,
constants.OPENFLOW12: 3,
constants.OPENFLOW13: 4,
constants.OPENFLOW14: 5
}
def version_from_protocol(protocol):
if protocol not in OF_PROTOCOL_TO_VERSION:
raise Exception("unknown OVS protocol string, cannot compare: %s, "
"(known: %s)" % (protocol,
list(OF_PROTOCOL_TO_VERSION)))
return OF_PROTOCOL_TO_VERSION[protocol]
class OVSBridge(BaseOVS):
def __init__(self, br_name, datapath_type=constants.OVS_DATAPATH_SYSTEM):
super(OVSBridge, self).__init__()
self.br_name = br_name
self.datapath_type = datapath_type
self._default_cookie = generate_random_cookie()
self._highest_protocol_needed = constants.OPENFLOW10
@property
def default_cookie(self):
@ -238,6 +257,13 @@ class OVSBridge(BaseOVS):
self.ovsdb.db_add('Bridge', self.br_name,
'protocols', *protocols).execute(check_error=True)
def use_at_least_protocol(self, protocol):
"""Calls to ovs-ofctl will use a protocol version >= 'protocol'"""
self.add_protocols(protocol)
self._highest_protocol_needed = max(self._highest_protocol_needed,
protocol,
key=version_from_protocol)
def create(self, secure_mode=False):
with self.ovsdb.transaction() as txn:
txn.add(
@ -249,7 +275,7 @@ class OVSBridge(BaseOVS):
# transactions
txn.add(
self.ovsdb.db_add('Bridge', self.br_name,
'protocols', constants.OPENFLOW10))
'protocols', self._highest_protocol_needed))
if secure_mode:
txn.add(self.ovsdb.set_fail_mode(self.br_name,
FAILMODE_SECURE))
@ -283,7 +309,9 @@ class OVSBridge(BaseOVS):
self.ovsdb.del_port(port_name, self.br_name).execute()
def run_ofctl(self, cmd, args, process_input=None):
full_args = ["ovs-ofctl", cmd, self.br_name] + args
full_args = ["ovs-ofctl", cmd,
"-O", self._highest_protocol_needed,
self.br_name] + args
# TODO(kevinbenton): This error handling is really brittle and only
# detects one specific type of failure. The callers of this need to
# be refactored to expect errors so we can re-raise and they can
@ -424,12 +452,12 @@ class OVSBridge(BaseOVS):
flows = self.run_ofctl("dump-flows", [flow_str])
if flows:
retval = '\n'.join(item for item in flows.splitlines()
if 'NXST' not in item)
if is_a_flow_line(item))
return retval
def dump_all_flows(self):
return [f for f in self.run_ofctl("dump-flows", []).splitlines()
if 'NXST' not in f]
if is_a_flow_line(f)]
def deferred(self, **kwargs):
return DeferredOVSBridge(self, **kwargs)
@ -888,3 +916,17 @@ def check_cookie_mask(cookie):
return cookie + '/-1'
else:
return cookie
def is_a_flow_line(line):
# this is used to filter out from ovs-ofctl dump-flows the lines that
# are not flow descriptions but mere indications of the type of openflow
# message that was used ; e.g.:
#
# # ovs-ofctl dump-flows br-int
# NXST_FLOW reply (xid=0x4):
# cookie=0xb7dff131a697c6a5, duration=2411726.809s, table=0, ...
# cookie=0xb7dff131a697c6a5, duration=2411726.786s, table=23, ...
# cookie=0xb7dff131a697c6a5, duration=2411726.760s, table=24, ...
#
return 'NXST' not in line and 'OFPST' not in line

View File

@ -239,7 +239,7 @@ class OVS_Lib_Test(base.BaseTestCase):
self.execute.assert_has_calls(expected_calls)
def _ofctl_args(self, cmd, *args):
cmd = ['ovs-ofctl', cmd]
cmd = ['ovs-ofctl', cmd, '-O', self.br._highest_protocol_needed]
cmd += args
return cmd
@ -444,6 +444,37 @@ class OVS_Lib_Test(base.BaseTestCase):
self.br.mod_flow,
**params)
def test_ofctl_of_version_use_highest(self):
self.br.add_flow(in_port=1, actions="drop")
self.execute.assert_has_calls([
mock.call(['ovs-ofctl', 'add-flows', '-O', p_const.OPENFLOW10,
mock.ANY, '-'], process_input=mock.ANY,
run_as_root=mock.ANY)
])
self.br.use_at_least_protocol(p_const.OPENFLOW12)
self.execute.reset_mock()
self.br.add_flow(in_port=1, actions="drop")
self.execute.assert_has_calls([
mock.call(['ovs-ofctl', 'add-flows', '-O', p_const.OPENFLOW12,
mock.ANY, '-'], process_input=mock.ANY,
run_as_root=mock.ANY),
])
def test_ofctl_of_version_keep_highest(self):
self.br.use_at_least_protocol(p_const.OPENFLOW13)
self.br.use_at_least_protocol(p_const.OPENFLOW12)
self.execute.reset_mock()
self.br.add_flow(in_port=1, actions="drop")
self.execute.assert_has_calls([
mock.call(['ovs-ofctl', 'add-flows', '-O', p_const.OPENFLOW13,
mock.ANY, '-'], process_input=mock.ANY,
run_as_root=mock.ANY),
])
def test_ofctl_of_version_use_unknown(self):
with testtools.ExpectedException(Exception):
self.br.use_at_least_protocol("OpenFlow42")
def test_run_ofctl_retry_on_socket_error(self):
err = RuntimeError('failed to connect to socket')
self.execute.side_effect = [err] * 5