diff options
author | Martin Klozik <martinx.klozik@intel.com> | 2015-12-11 14:29:18 +0000 |
---|---|---|
committer | Maryam Tahhan <maryam.tahhan@intel.com> | 2016-01-21 09:40:34 +0000 |
commit | 7caf7a6be0fae6b341181b3e6286372e2e93b4b8 (patch) | |
tree | 2a8284cc39b85eb095a0c5a4763fd2e558d166c2 | |
parent | 2a3ff071bd08ff02610286a5ad4ba46690b6bc6b (diff) |
testcase: scalability - configurable installation of flows to the vswitch
Stream specific flows can be pre-installed into the vswitch
based on the value of testcase specific configuration option
"Pre-installed Flows". In case, it is set to 'Yes', then
specific flow for each stream will be inserted into the switch.
Otherwise only generic flows will be installed. Default value
of "Pre-installed Flows" is set to 'No'. Its value can be
overridden by CLI parameter pre-installed_flows.
This configuration parameter is an enhancement of "MultiSream"
feature and it is ignored if "MultiStream" is disabled.
Python module 'netaddr' is required by this implementation
and it has been added to requirements.txt file.
Change-Id: I8a17577a702bf2be2753134eb203b936a87fc2e5
JIRA: VSPERF-83
Signed-off-by: Martin Klozik <martinx.klozik@intel.com>
Reviewed-by: Maryam Tahhan <maryam.tahhan@intel.com>
Reviewed-by: Brian Castelli <brian.castelli@spirent.com>
-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 = { |