diff options
-rwxr-xr-x | conf/01_testcases.conf | 12 | ||||
-rw-r--r-- | core/vswitch_controller_p2p.py | 49 | ||||
-rw-r--r-- | requirements.txt | 1 | ||||
-rw-r--r-- | src/ovs/ofctl.py | 34 | ||||
-rw-r--r-- | testcases/testcase.py | 3 | ||||
-rw-r--r-- | vswitches/ovs_dpdk_vhost.py | 4 | ||||
-rw-r--r-- | vswitches/ovs_vanilla.py | 4 | ||||
-rw-r--r-- | vswitches/vswitch.py | 9 |
8 files changed, 102 insertions, 14 deletions
diff --git a/conf/01_testcases.conf b/conf/01_testcases.conf index fd268bed..b855405d 100755 --- a/conf/01_testcases.conf +++ b/conf/01_testcases.conf @@ -43,6 +43,18 @@ # # "L3" - iteration of destination IP address # # "L4" - iteration of destination UDP port # # Default value is "L4". +# "Pre-installed Flows": ["Yes"|"No"] +# # Optional. Pre-installed Flows is an extension +# # of the "MultiStream" feature. If MultiStream +# # is disabled, then Pre-installed Flows will be +# # ignored. It defines if stream specific flows +# # will be inserted into OVS or not. +# # It can be overridden by cli option +# # pre-installed_flows +# # Values: +# # "Yes" - flows will be inserted into OVS +# # "No" - flows won't be inserted into OVS +# # Default value is "No". # "Flow Type": ["port"|"IP"] # Optional. Defines flows complexity. In case # # it isn't specified, then "port" will be used. # # Values: diff --git a/core/vswitch_controller_p2p.py b/core/vswitch_controller_p2p.py index 236a443a..91c4e8a0 100644 --- a/core/vswitch_controller_p2p.py +++ b/core/vswitch_controller_p2p.py @@ -16,6 +16,7 @@ """ import logging +import netaddr from core.vswitch_controller import IVswitchController from conf import settings @@ -24,6 +25,9 @@ _FLOW_TEMPLATE = { 'idle_timeout': '0' } +_PROTO_TCP = 6 +_PROTO_UDP = 17 + class VswitchControllerP2P(IVswitchController): """VSwitch controller for P2P deployment scenario. @@ -79,12 +83,12 @@ class VswitchControllerP2P(IVswitchController): flow.update({'table':'1', 'priority':'1', 'in_port':'1', 'actions': ['write_actions(output:2)', 'write_metadata:2', 'goto_table:2']}) - self._vswitch.add_flow(bridge, flow) + self.process_flow_template(bridge, flow) flow = flow_template.copy() flow.update({'table':'1', 'priority':'1', 'in_port':'2', 'actions': ['write_actions(output:1)', 'write_metadata:1', 'goto_table:2']}) - self._vswitch.add_flow(bridge, flow) + self.process_flow_template(bridge, flow) # Frame modification table. Frame modification flow rules are # isolated in this table so that they can be turned on or off @@ -129,3 +133,44 @@ class VswitchControllerP2P(IVswitchController): """See IVswitchController for description """ self._vswitch.dump_flows(settings.getValue('VSWITCH_BRIDGE_NAME')) + + def process_flow_template(self, bridge, flow_template): + """Method adds flows into the vswitch based on given flow template + and configuration of multistream feature. + """ + if ('pre_installed_flows' in self._traffic and + self._traffic['pre_installed_flows'].lower() == 'yes' and + 'multistream' in self._traffic and self._traffic['multistream'] > 0 and + 'stream_type' in self._traffic): + # multistream feature is enabled and flows should be inserted into OVS + # so generate flows based on template and multistream configuration + if self._traffic['stream_type'] == 'L2': + # iterate through destimation MAC address + dst_mac_value = netaddr.EUI(self._traffic['l2']['dstmac']).value + for i in range(int(self._traffic['multistream'])): + tmp_mac = netaddr.EUI(dst_mac_value + i) + tmp_mac.dialect = netaddr.mac_unix_expanded + flow_template.update({'dl_dst':tmp_mac}) + # optimize flow insertion by usage of cache + self._vswitch.add_flow(bridge, flow_template, cache='on') + elif self._traffic['stream_type'] == 'L3': + # iterate through destimation IP address + dst_ip_value = netaddr.IPAddress(self._traffic['l3']['dstip']).value + for i in range(int(self._traffic['multistream'])): + tmp_ip = netaddr.IPAddress(dst_ip_value + i) + flow_template.update({'dl_type':'0x800', 'nw_dst':tmp_ip}) + # optimize flow insertion by usage of cache + self._vswitch.add_flow(bridge, flow_template, cache='on') + elif self._traffic['stream_type'] == 'L4': + # read transport protocol from configuration and iterate through its destination port + proto = _PROTO_TCP if self._traffic['l3']['proto'].lower() == 'tcp' else _PROTO_UDP + for i in range(int(self._traffic['multistream'])): + flow_template.update({'dl_type':'0x800', 'nw_proto':proto, 'tp_dst':i}) + # optimize flow insertion by usage of cache + self._vswitch.add_flow(bridge, flow_template, cache='on') + else: + self._logger.error('Stream type is set to uknown value %s', self._traffic['stream_type']) + # insert cached flows into the OVS + self._vswitch.add_flow(bridge, [], cache='flush') + else: + self._vswitch.add_flow(bridge, flow_template) diff --git a/requirements.txt b/requirements.txt index 39f0a090..16b7ba1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ tox==1.8.1 jinja2==2.7.3 xmlrunner==1.7.7 requests==2.8.1 +netaddr==0.7.18 diff --git a/src/ovs/ofctl.py b/src/ovs/ofctl.py index c052f85c..9d16ef76 100644 --- a/src/ovs/ofctl.py +++ b/src/ovs/ofctl.py @@ -34,6 +34,8 @@ _OVS_OFCTL_BIN = os.path.join(settings.getValue('OVS_DIR'), 'utilities', _OVS_BRIDGE_NAME = settings.getValue('VSWITCH_BRIDGE_NAME') +_CACHE_FILE_NAME = '/tmp/vsperf_flows_cache' + class OFBase(object): """Add/remove/show datapaths using ``ovs-ofctl``. """ @@ -103,6 +105,7 @@ class OFBridge(OFBase): super(OFBridge, self).__init__(timeout) self.br_name = br_name self._ports = {} + self._cache_file = None # context manager @@ -121,7 +124,7 @@ class OFBridge(OFBase): # helpers - def run_ofctl(self, args, check_error=False): + def run_ofctl(self, args, check_error=False, timeout=None): """Run ``ovs-ofctl`` with supplied arguments. :param args: Arguments to pass to ``ovs-ofctl`` @@ -129,8 +132,9 @@ class OFBridge(OFBase): :return: None """ + tmp_timeout = self.timeout if timeout == None else timeout cmd = ['sudo', _OVS_OFCTL_BIN, '-O', 'OpenFlow13', '--timeout', - str(self.timeout)] + args + str(tmp_timeout)] + args return tasks.run_task( cmd, self.logger, 'Running ovs-ofctl...', check_error) @@ -233,7 +237,7 @@ class OFBridge(OFBase): # flow mangement - def add_flow(self, flow): + def add_flow(self, flow, cache='off'): """Add flow to bridge. :param flow: Flow description as a dictionary @@ -241,14 +245,32 @@ class OFBridge(OFBase): :return: None """ + # insert flows from cache into OVS if needed + if cache == 'flush': + if self._cache_file == None: + self.logger.error('flow cache flush called, but nothing is cached') + return + self.logger.debug('flows cached in %s will be added to the bridge', _CACHE_FILE_NAME) + self._cache_file.close() + self._cache_file = None + self.run_ofctl(['add-flows', self.br_name, _CACHE_FILE_NAME], timeout=600) + return + if not flow.get('actions'): self.logger.error('add flow requires actions') return - self.logger.debug('add flow') _flow_key = flow_key(flow) self.logger.debug('key : %s', _flow_key) - self.run_ofctl(['add-flow', self.br_name, _flow_key]) + + # insert flow to the cache or OVS + if cache == 'on': + # create and open cache file if needed + if self._cache_file == None: + self._cache_file = open(_CACHE_FILE_NAME, 'w') + self._cache_file.write(_flow_key + '\n') + else: + self.run_ofctl(['add-flow', self.br_name, _flow_key]) def del_flow(self, flow): """Delete flow from bridge. @@ -274,7 +296,7 @@ class OFBridge(OFBase): """Dump all flows from bridge. """ self.logger.debug('dump flows') - self.run_ofctl(['dump-flows', self.br_name]) + self.run_ofctl(['dump-flows', self.br_name], timeout=120) # # helper functions diff --git a/testcases/testcase.py b/testcases/testcase.py index 986fbe4b..a152f91e 100644 --- a/testcases/testcase.py +++ b/testcases/testcase.py @@ -70,7 +70,8 @@ class TestCase(object): multistream = get_test_param('multistream', multistream) stream_type = cfg.get('Stream Type', 'L4') stream_type = get_test_param('stream_type', stream_type) - pre_installed_flows = False # placeholder for VSPERF-83 implementation + pre_installed_flows = cfg.get('Pre-installed Flows', 'No') + pre_installed_flows = get_test_param('pre-installed_flows', pre_installed_flows) # check if test requires background load and which generator it uses diff --git a/vswitches/ovs_dpdk_vhost.py b/vswitches/ovs_dpdk_vhost.py index cf60a5e2..2c7b81ff 100644 --- a/vswitches/ovs_dpdk_vhost.py +++ b/vswitches/ovs_dpdk_vhost.py @@ -148,11 +148,11 @@ class OvsDpdkVhost(IVSwitch): bridge = self._bridges[switch_name] bridge.del_port(port_name) - def add_flow(self, switch_name, flow): + def add_flow(self, switch_name, flow, cache='off'): """See IVswitch for general description """ bridge = self._bridges[switch_name] - bridge.add_flow(flow) + bridge.add_flow(flow, cache=cache) def del_flow(self, switch_name, flow=None): """See IVswitch for general description diff --git a/vswitches/ovs_vanilla.py b/vswitches/ovs_vanilla.py index c6617404..3078de02 100644 --- a/vswitches/ovs_vanilla.py +++ b/vswitches/ovs_vanilla.py @@ -164,11 +164,11 @@ class OvsVanilla(IVSwitch): bridge = self._bridges[switch_name] bridge.del_port(port_name) - def add_flow(self, switch_name, flow): + def add_flow(self, switch_name, flow, cache='off'): """See IVswitch for general description """ bridge = self._bridges[switch_name] - bridge.add_flow(flow) + bridge.add_flow(flow, cache=cache) def del_flow(self, switch_name, flow=None): """See IVswitch for general description diff --git a/vswitches/vswitch.py b/vswitches/vswitch.py index fbec861a..a28c0f6b 100644 --- a/vswitches/vswitch.py +++ b/vswitches/vswitch.py @@ -89,11 +89,18 @@ class IVSwitch(object): """ raise NotImplementedError() - def add_flow(self, switch_name, flow): + def add_flow(self, switch_name, flow, cache='off'): """Add a flow rule to the logical switch :param switch_name: The switch on which to operate :param flow: Flow description as a dictionary + :param cache: Optional. Specifies if flow should be inserted + to the switch or cached to increase performance during manipulation + with large number of flows. + Values: + 'off' - cache is off and flow is inserted directly to the switch + 'on' - cache is on and flow is inserted into the cache + 'flush' - cache content will be inserted into the switch Example flow dictionary: flow = { |