aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--conf/integration/01_testcases.conf168
-rw-r--r--core/component_factory.py3
-rw-r--r--core/traffic_controller_rfc2544.py13
-rw-r--r--core/vswitch_controller_clean.py79
-rw-r--r--core/vswitch_controller_p2p.py4
-rwxr-xr-xdocs/userguide/integration.rst22
-rw-r--r--src/ovs/ofctl.py24
-rw-r--r--testcases/__init__.py2
-rw-r--r--testcases/integration.py173
-rw-r--r--testcases/performance.py38
-rw-r--r--testcases/testcase.py106
-rw-r--r--tools/report/report.py18
-rwxr-xr-xvsperf24
-rw-r--r--vswitches/ovs.py229
-rw-r--r--vswitches/ovs_dpdk_vhost.py130
-rw-r--r--vswitches/ovs_vanilla.py129
16 files changed, 864 insertions, 298 deletions
diff --git a/conf/integration/01_testcases.conf b/conf/integration/01_testcases.conf
index 5e9fc66d..fff148d4 100644
--- a/conf/integration/01_testcases.conf
+++ b/conf/integration/01_testcases.conf
@@ -27,6 +27,16 @@ SUPPORTED_TUNNELING_PROTO = ['vxlan', 'gre', 'geneve']
#
# biDirectional testing for OP2P is not yet supported.
# biDirectional must be set to False.
+#
+# "TestSteps": [] # Definition of integration test steps.
+# # In case that this list is defined, then
+# # vsperf will execute defined test steps
+# # one by one. It can be used to configure
+# # vswitch, insert flows and transmit traffic.
+# # It is possible to refer to result of any
+# # previous step through #STEP[i][j] macro.
+# # Where i is a number of step (starts from 0)
+# # and j is index of result returned by step i.
INTEGRATION_TESTS = [
{
@@ -65,5 +75,163 @@ INTEGRATION_TESTS = [
"Tunnel Operation": "decapsulation",
"Description": "Overlay Decapsulation Continuous Stream",
},
+ {
+ "Name": "vswitch_add_del_bridge",
+ "Deployment": "clean",
+ "Description": "vSwitch - add and delete bridge",
+ "TestSteps": [
+ ['vswitch', 'add_switch', 'int_br0'],
+ ['vswitch', 'del_switch', 'int_br0'],
+ ]
+ },
+ {
+ "Name": "vswitch_add_del_bridges",
+ "Deployment": "clean",
+ "Description": "vSwitch - add and delete bridges",
+ "TestSteps": [
+ ['vswitch', 'add_switch', 'int_br0'],
+ ['vswitch', 'add_switch', 'int_br1'],
+ ['vswitch', 'del_switch', 'int_br0'],
+ ['vswitch', 'del_switch', 'int_br1'],
+ ]
+ },
+ {
+ "Name": "vswitch_add_del_phy_port",
+ "Deployment": "clean",
+ "Description": "vSwitch - add and delete physical port",
+ "TestSteps": [
+ ['vswitch', 'add_switch', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'],
+ ['vswitch', 'del_switch', 'int_br0'],
+ ]
+ },
+ {
+ "Name": "vswitch_add_del_phy_ports",
+ "Deployment": "clean",
+ "Description": "vSwitch - add and delete physical ports",
+ "TestSteps": [
+ ['vswitch', 'add_switch', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'],
+ ['vswitch', 'del_switch', 'int_br0'],
+ ]
+ },
+ {
+ "Name": "vswitch_add_del_vport",
+ "Deployment": "clean",
+ "Description": "vSwitch - add and delete virtual port",
+ "TestSteps": [
+ ['vswitch', 'add_switch', 'int_br0'],
+ ['vswitch', 'add_vport', 'int_br0'],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'],
+ ['vswitch', 'del_switch', 'int_br0'],
+ ]
+ },
+ {
+ "Name": "vswitch_add_del_vports",
+ "Deployment": "clean",
+ "Description": "vSwitch - add and delete virtual ports",
+ "TestSteps": [
+ ['vswitch', 'add_switch', 'int_br0'],
+ ['vswitch', 'add_vport', 'int_br0'],
+ ['vswitch', 'add_vport', 'int_br0'],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'],
+ ['vswitch', 'del_switch', 'int_br0'],
+ ]
+ },
+ {
+ "Name": "vswitch_add_del_flow",
+ "Deployment": "clean",
+ "Description": "vSwitch - add and delete flow",
+ "TestSteps": [
+ ['vswitch', 'add_switch', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}],
+ ['vswitch', 'del_flow', 'int_br0', {'in_port': '#STEP[1][1]'}],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'],
+ ['vswitch', 'del_switch', 'int_br0'],
+ ]
+ },
+ {
+ "Name": "vswitch_add_del_flows",
+ "Deployment": "clean",
+ "Description": "vSwitch - add and delete flows",
+ "TestSteps": [
+ ['vswitch', 'add_switch', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}],
+ ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[2][1]', 'actions': ['output:#STEP[1][1]'], 'idle_timeout': '0'}],
+ ['vswitch', 'dump_flows', 'int_br0'],
+ ['vswitch', 'del_flow', 'int_br0', {'in_port': '#STEP[1][1]'}],
+ ['vswitch', 'del_flow', 'int_br0', {'in_port': '#STEP[2][1]'}],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'],
+ ['vswitch', 'del_switch', 'int_br0'],
+ ]
+ },
+ {
+ "Name": "vswitch_throughput",
+ "Deployment": "clean",
+ "Description": "vSwitch - configure switch and execute RFC2544 throughput test",
+ "TestSteps": [
+ ['vswitch', 'add_switch', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}],
+ ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[2][1]', 'actions': ['output:#STEP[1][1]'], 'idle_timeout': '0'}],
+ ['trafficgen', 'send_traffic', {'traffic_type' : 'throughput', 'bidir' : True, 'frame_rate' : 100, 'multistream' : 0, 'stream_type' : 'L4'}],
+ ['vswitch', 'dump_flows', 'int_br0'],
+ ['vswitch', 'del_flow', 'int_br0', {'in_port': '#STEP[1][1]'}],
+ ['vswitch', 'del_flow', 'int_br0', {'in_port': '#STEP[2][1]'}],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'],
+ ['vswitch', 'del_switch', 'int_br0'],
+ ]
+ },
+ {
+ "Name": "vswitch_back2back",
+ "Deployment": "clean",
+ "Description": "vSwitch - configure switch and execute RFC2544 back2back test",
+ "TestSteps": [
+ ['vswitch', 'add_switch', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}],
+ ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[2][1]', 'actions': ['output:#STEP[1][1]'], 'idle_timeout': '0'}],
+ ['trafficgen', 'send_traffic', {'traffic_type' : 'back2back', 'bidir' : True, 'frame_rate' : 100, 'multistream' : 0, 'stream_type' : 'L4'}],
+ ['vswitch', 'dump_flows', 'int_br0'],
+ ['vswitch', 'del_flow', 'int_br0', {'in_port': '#STEP[1][1]'}],
+ ['vswitch', 'del_flow', 'int_br0', {'in_port': '#STEP[2][1]'}],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'],
+ ['vswitch', 'del_switch', 'int_br0'],
+ ]
+ },
+ {
+ "Name": "vswitch_continuous",
+ "Deployment": "clean",
+ "Description": "vSwitch - configure switch and execute continuous stream test",
+ "TestSteps": [
+ ['vswitch', 'add_switch', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'add_phy_port', 'int_br0'],
+ ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[1][1]', 'actions': ['output:#STEP[2][1]'], 'idle_timeout': '0'}],
+ ['vswitch', 'add_flow', 'int_br0', {'in_port': '#STEP[2][1]', 'actions': ['output:#STEP[1][1]'], 'idle_timeout': '0'}],
+ ['trafficgen', 'send_traffic', {'traffic_type' : 'continuous', 'bidir' : True, 'frame_rate' : 100, 'multistream' : 0, 'stream_type' : 'L4'}],
+ ['vswitch', 'dump_flows', 'int_br0'],
+ ['vswitch', 'del_flow', 'int_br0', {'in_port': '#STEP[1][1]'}],
+ ['vswitch', 'del_flow', 'int_br0', {'in_port': '#STEP[2][1]'}],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[1][0]'],
+ ['vswitch', 'del_port', 'int_br0', '#STEP[2][0]'],
+ ['vswitch', 'del_switch', 'int_br0'],
+ ]
+ },
]
diff --git a/core/component_factory.py b/core/component_factory.py
index cb5af211..9c58fc5c 100644
--- a/core/component_factory.py
+++ b/core/component_factory.py
@@ -16,6 +16,7 @@
"""
from core.traffic_controller_rfc2544 import TrafficControllerRFC2544
+from core.vswitch_controller_clean import VswitchControllerClean
from core.vswitch_controller_p2p import VswitchControllerP2P
from core.vswitch_controller_pvp import VswitchControllerPVP
from core.vswitch_controller_pvvp import VswitchControllerPVVP
@@ -72,6 +73,8 @@ def create_vswitch(deployment_scenario, vswitch_class, traffic,
return VswitchControllerPVVP(vswitch_class, traffic)
elif deployment_scenario.find("op2p") >= 0:
return VswitchControllerOP2P(vswitch_class, traffic, tunnel_operation)
+ elif deployment_scenario.find("clean") >= 0:
+ return VswitchControllerClean(vswitch_class, traffic)
def create_vnf(deployment_scenario, vnf_class):
diff --git a/core/traffic_controller_rfc2544.py b/core/traffic_controller_rfc2544.py
index 020b4aec..2630101f 100644
--- a/core/traffic_controller_rfc2544.py
+++ b/core/traffic_controller_rfc2544.py
@@ -154,3 +154,16 @@ class TrafficControllerRFC2544(ITrafficController, IResults):
"""IResult interface implementation.
"""
return self._results
+
+ def validate_send_traffic(self, result, traffic):
+ """Verify that send traffic has succeeded
+ """
+ if len(self._results):
+ if 'b2b_frames' in self._results[-1]:
+ return float(self._results[-1]['b2b_frames']) > 0
+ elif 'throughput_rx_fps' in self._results[-1]:
+ return float(self._results[-1]['throughput_rx_fps']) > 0
+ else:
+ return True
+ else:
+ return False
diff --git a/core/vswitch_controller_clean.py b/core/vswitch_controller_clean.py
new file mode 100644
index 00000000..61724b9b
--- /dev/null
+++ b/core/vswitch_controller_clean.py
@@ -0,0 +1,79 @@
+# Copyright 2015-2016 Intel Corporation.
+#
+# 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.
+
+"""VSwitch controller for basic initialization of vswitch
+"""
+
+import logging
+
+from core.vswitch_controller import IVswitchController
+
+class VswitchControllerClean(IVswitchController):
+ """VSwitch controller for Clean deployment scenario.
+
+ Attributes:
+ _vswitch_class: The vSwitch class to be used.
+ _vswitch: The vSwitch object controlled by this controller
+ _deployment_scenario: A string describing the scenario to set-up in the
+ constructor.
+ """
+ def __init__(self, vswitch_class, traffic):
+ """Initializes up the prerequisites for the Clean deployment scenario.
+
+ :vswitch_class: the vSwitch class to be used.
+ """
+ self._logger = logging.getLogger(__name__)
+ self._vswitch_class = vswitch_class
+ self._vswitch = vswitch_class()
+ self._deployment_scenario = "Clean"
+ self._logger.debug('Creation using ' + str(self._vswitch_class))
+ self._traffic = traffic.copy()
+
+ def setup(self):
+ """Sets up the switch for Clean.
+ """
+ self._logger.debug('Setup using ' + str(self._vswitch_class))
+
+ try:
+ self._vswitch.start()
+ except:
+ self._vswitch.stop()
+ raise
+
+ def stop(self):
+ """Tears down the switch created in setup().
+ """
+ self._logger.debug('Stop using ' + str(self._vswitch_class))
+ self._vswitch.stop()
+
+ def __enter__(self):
+ self.setup()
+
+ def __exit__(self, type_, value, traceback):
+ self.stop()
+
+ def get_vswitch(self):
+ """See IVswitchController for description
+ """
+ return self._vswitch
+
+ def get_ports_info(self):
+ """See IVswitchController for description
+ """
+ pass
+
+ def dump_vswitch_flows(self):
+ """See IVswitchController for description
+ """
+ pass
diff --git a/core/vswitch_controller_p2p.py b/core/vswitch_controller_p2p.py
index 91c4e8a0..e9ab5cc4 100644
--- a/core/vswitch_controller_p2p.py
+++ b/core/vswitch_controller_p2p.py
@@ -81,12 +81,12 @@ class VswitchControllerP2P(IVswitchController):
flow = flow_template.copy()
flow.update({'table':'1', 'priority':'1', 'in_port':'1',
- 'actions': ['write_actions(output:2)', 'write_metadata:2',
+ 'actions': ['write_actions(output:2)', 'write_metadata:0x2',
'goto_table:2']})
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',
+ 'actions': ['write_actions(output:1)', 'write_metadata:0x1',
'goto_table:2']})
self.process_flow_template(bridge, flow)
diff --git a/docs/userguide/integration.rst b/docs/userguide/integration.rst
index dc871280..27bf2cd0 100755
--- a/docs/userguide/integration.rst
+++ b/docs/userguide/integration.rst
@@ -6,7 +6,7 @@ Integration tests
=================
VSPERF includes a set of integration tests defined in conf/integration.
-These tests can be run by specifying --run-integration as a parameter to vsperf.
+These tests can be run by specifying --integration as a parameter to vsperf.
Current tests in conf/integration are Overlay tests.
VSPERF supports VXLAN, GRE and GENEVE tunneling protocols.
@@ -59,21 +59,21 @@ To run VXLAN encapsulation tests:
.. code-block:: console
- ./vsperf --conf-file user_settings.py --run-integration
+ ./vsperf --conf-file user_settings.py --integration
--test-params 'tunnel_type=vxlan' overlay_p2p_tput
To run GRE encapsulation tests:
.. code-block:: console
- ./vsperf --conf-file user_settings.py --run-integration
+ ./vsperf --conf-file user_settings.py --integration
--test-params 'tunnel_type=gre' overlay_p2p_tput
To run GENEVE encapsulation tests:
.. code-block:: console
- ./vsperf --conf-file user_settings.py --run-integration
+ ./vsperf --conf-file user_settings.py --integration
--test-params 'tunnel_type=geneve' overlay_p2p_tput
To run OVS NATIVE tunnel tests (VXLAN/GRE/GENEVE):
@@ -102,7 +102,7 @@ To run OVS NATIVE tunnel tests (VXLAN/GRE/GENEVE):
.. code-block:: console
- ./vsperf --conf-file user_settings.py --run-integration
+ ./vsperf --conf-file user_settings.py --integration
--test-params 'tunnel_type=vxlan' overlay_p2p_tput
@@ -123,7 +123,7 @@ To run VXLAN decapsulation tests:
.. code-block:: console
- ./vsperf --conf-file user_settings.py --run-integration overlay_p2p_decap_cont
+ ./vsperf --conf-file user_settings.py --integration overlay_p2p_decap_cont
If you want to use different values for your VXLAN frame, you may set:
@@ -165,7 +165,7 @@ To run GRE decapsulation tests:
.. code-block:: console
./vsperf --conf-file user_settings.py --test-params 'tunnel_type=gre'
- --run-integration overlay_p2p_decap_cont
+ --integration overlay_p2p_decap_cont
If you want to use different values for your GRE frame, you may set:
@@ -222,7 +222,7 @@ To run GENEVE decapsulation tests:
.. code-block:: console
./vsperf --conf-file user_settings.py --test-params 'tunnel_type=geneve'
- --run-integration overlay_p2p_decap_cont
+ --integration overlay_p2p_decap_cont
If you want to use different values for your GENEVE frame, you may set:
@@ -301,7 +301,7 @@ To run VXLAN decapsulation tests:
.. code-block:: console
- ./vsperf --conf-file user_settings.py --run-integration
+ ./vsperf --conf-file user_settings.py --integration
--test-params 'tunnel_type=vxlan' overlay_p2p_decap_cont
Executing Native/Vanilla OVS GRE decapsulation tests
@@ -356,7 +356,7 @@ To run GRE decapsulation tests:
.. code-block:: console
- ./vsperf --conf-file user_settings.py --run-integration
+ ./vsperf --conf-file user_settings.py --integration
--test-params 'tunnel_type=gre' overlay_p2p_decap_cont
Executing Native/Vanilla OVS GENEVE decapsulation tests
@@ -411,6 +411,6 @@ To run GENEVE decapsulation tests:
.. code-block:: console
- ./vsperf --conf-file user_settings.py --run-integration
+ ./vsperf --conf-file user_settings.py --integration
--test-params 'tunnel_type=geneve' overlay_p2p_decap_cont
diff --git a/src/ovs/ofctl.py b/src/ovs/ofctl.py
index 43151d3a..93894889 100644
--- a/src/ovs/ofctl.py
+++ b/src/ovs/ofctl.py
@@ -23,6 +23,7 @@ https://github.com/openstack/neutron/blob/6eac1dc99124ca024d6a69b3abfa3bc69c7356
import os
import logging
import string
+import re
from tools import tasks
from conf import settings
@@ -389,3 +390,26 @@ def flow_key(flow):
flow_str = _flow_del_key.substitute(_flow_key_param)
return flow_str
+
+def flow_match(flow_dump, flow_src):
+ """ Compares two flows
+
+ :param flow_dump: string - a string with flow obtained by ovs-ofctl dump-flows
+ :param flow_src: string - a string with flow obtained by call of flow_key()
+
+ :return: boolean
+ """
+ # perform unifications on both source and destination flows
+ flow_dump = flow_dump.replace('actions=', 'action=')
+ flow_src = flow_src.replace('actions=', 'action=')
+
+ # split flow strings into lists of comparable elements
+ flow_dump_list = re.findall(r"[\w.:=()]+", flow_dump)
+ flow_src_list = re.findall(r"[\w.:=()]+", flow_src)
+
+ # check if all items from source flow are present in dump flow
+ flow_src_ctrl = list(flow_src_list)
+ for rule in flow_src_list:
+ if rule in flow_dump_list:
+ flow_src_ctrl.remove(rule)
+ return True if not len(flow_src_ctrl) else False
diff --git a/testcases/__init__.py b/testcases/__init__.py
index addf63df..0b6b77e4 100644
--- a/testcases/__init__.py
+++ b/testcases/__init__.py
@@ -15,3 +15,5 @@
"""This module contains test definitions.
"""
from testcases.testcase import (TestCase)
+from testcases.performance import (PerformanceTestCase)
+from testcases.integration import (IntegrationTestCase)
diff --git a/testcases/integration.py b/testcases/integration.py
new file mode 100644
index 00000000..ecaed14f
--- /dev/null
+++ b/testcases/integration.py
@@ -0,0 +1,173 @@
+# Copyright 2015-2016 Intel Corporation.
+#
+# 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.
+"""IntegrationTestCase class
+"""
+
+import os
+import time
+import logging
+
+from testcases import TestCase
+from conf import settings as S
+from collections import OrderedDict
+
+CHECK_PREFIX = 'validate_'
+
+class IntegrationTestCase(TestCase):
+ """IntegrationTestCase class
+ """
+
+ def __init__(self, cfg, results_dir):
+ """ Testcase initialization
+ """
+ self._type = 'integration'
+ super(IntegrationTestCase, self).__init__(cfg, results_dir)
+ self._logger = logging.getLogger(__name__)
+ self._inttest = None
+
+ def report_status(self, label, status):
+ """ Log status of test step
+ """
+ self._logger.debug("%s ... %s", label, 'OK' if status else 'FAILED')
+
+ def run_initialize(self):
+ """ Prepare test execution environment
+ """
+ super(IntegrationTestCase, self).run_initialize()
+ self._inttest = {'status' : True, 'details' : ''}
+
+ def run(self):
+ """Run the test
+
+ All setup and teardown through controllers is included.
+ """
+ def eval_step_params(params, step_result):
+ """ Evaluates referrences to results from previous steps
+ """
+ def eval_param(param, STEP):
+ """ Helper function
+ """
+ if isinstance(param, str):
+ tmp_param = ''
+ # evaluate every #STEP reference inside parameter itself
+ for chunk in param.split('#'):
+ if chunk.startswith('STEP['):
+ tmp_param = tmp_param + str(eval(chunk))
+ else:
+ tmp_param = tmp_param + chunk
+ return tmp_param
+ elif isinstance(param, list) or isinstance(param, tuple):
+ tmp_list = []
+ for item in param:
+ tmp_list.append(eval_param(item, STEP))
+ return tmp_list
+ elif isinstance(param, dict):
+ tmp_dict = {}
+ for (key, value) in param.items():
+ tmp_dict[key] = eval_param(value, STEP)
+ return tmp_dict
+ else:
+ return param
+
+ eval_params = []
+ # evaluate all parameters if needed
+ for param in params:
+ eval_params.append(eval_param(param, step_result))
+ return eval_params
+
+ # prepare test execution environment
+ self.run_initialize()
+
+ with self._vswitch_ctl, self._loadgen:
+ with self._vnf_ctl, self._collector:
+ if not self._vswitch_none:
+ self._add_flows()
+
+ # run traffic generator if requested, otherwise wait for manual termination
+ if S.getValue('mode') == 'trafficgen-off':
+ time.sleep(2)
+ self._logger.debug("All is set. Please run traffic generator manually.")
+ input(os.linesep + "Press Enter to terminate vswitchperf..." + os.linesep + os.linesep)
+ else:
+ with self._traffic_ctl:
+ if not self.test:
+ self._traffic_ctl.send_traffic(self._traffic)
+ else:
+ # execute test based on TestSteps definition
+ if self.test:
+ step_result = [None] * len(self.test)
+ for i, step in enumerate(self.test):
+ step_ok = False
+ if step[0] == 'vswitch':
+ test_object = self._vswitch_ctl.get_vswitch()
+ elif step[0] == 'trafficgen':
+ test_object = self._traffic_ctl
+ else:
+ self._logger.error("Unsupported test object %s", step[0])
+ self._inttest = {'status' : False, 'details' : ' '.join(step)}
+ self.report_status("Step '{}'".format(' '.join(step)), self._inttest['status'])
+ break
+
+ test_method = getattr(test_object, step[1])
+ test_method_check = getattr(test_object, CHECK_PREFIX + step[1])
+
+ step_params = []
+ if test_method and test_method_check and \
+ callable(test_method) and callable(test_method_check):
+
+ try:
+ step_params = eval_step_params(step[2:], step_result)
+ step_log = '{} {}'.format(' '.join(step[:2]), step_params)
+ step_result[i] = test_method(*step_params)
+ self._logger.debug("Step {} '{}' results '{}'".format(
+ i, step_log, step_result[i]))
+ time.sleep(2)
+ step_ok = test_method_check(step_result[i], *step_params)
+ except AssertionError:
+ self._inttest = {'status' : False, 'details' : step_log}
+ self._logger.error("Step {} raised assertion error".format(i))
+ break
+ except IndexError:
+ self._inttest = {'status' : False, 'details' : step_log}
+ self._logger.error("Step {} result index error {}".format(
+ i, ' '.join(step[2:])))
+ break
+
+ self.report_status("Step {} - '{}'".format(i, step_log), step_ok)
+ if not step_ok:
+ self._inttest = {'status' : False, 'details' : step_log}
+ break
+
+ # dump vswitch flows before they are affected by VNF termination
+ if not self._vswitch_none:
+ self._vswitch_ctl.dump_vswitch_flows()
+
+ # tear down test execution environment and log results
+ self.run_finalize()
+
+ # report test results
+ self.run_report()
+
+ def run_report(self):
+ """ Report test results
+ """
+ if self.test:
+ results = OrderedDict()
+ results['status'] = 'OK' if self._inttest['status'] else 'FAILED'
+ results['details'] = self._inttest['details']
+ TestCase._write_result_to_file([results], self._output_file)
+ self.report_status("Test '{}'".format(self.name), self._inttest['status'])
+ # inform vsperf about testcase failure
+ if not self._inttest['status']:
+ raise Exception
diff --git a/testcases/performance.py b/testcases/performance.py
new file mode 100644
index 00000000..0ae3ea77
--- /dev/null
+++ b/testcases/performance.py
@@ -0,0 +1,38 @@
+# Copyright 2015-2016 Intel Corporation.
+#
+# 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.
+"""PerformanceTestCase class
+"""
+
+import logging
+
+from testcases import TestCase
+from tools.report import report
+from conf import settings as S
+
+class PerformanceTestCase(TestCase):
+ """PerformanceTestCase class
+
+ In this basic form runs RFC2544 throughput test
+ """
+ def __init__(self, cfg, results_dir):
+ """ Testcase initialization
+ """
+ self._type = 'performance'
+ super(PerformanceTestCase, self).__init__(cfg, results_dir)
+ self._logger = logging.getLogger(__name__)
+
+ def run_report(self):
+ super(PerformanceTestCase, self).run_report()
+ if S.getValue('mode') != 'trafficgen-off':
+ report.generate(self._output_file, self._tc_results, self._collector.get_results(), self._type)
diff --git a/testcases/testcase.py b/testcases/testcase.py
index dfc766d2..0effce75 100644
--- a/testcases/testcase.py
+++ b/testcases/testcase.py
@@ -16,18 +16,17 @@
import csv
import os
+import time
import logging
import subprocess
import copy
-import time
from collections import OrderedDict
-from core.results.results_constants import ResultsConstants
import core.component_factory as component_factory
from core.loader import Loader
+from core.results.results_constants import ResultsConstants
from tools import tasks
from tools import hugepages
-from tools.report import report
from tools.pkt_gen.trafficgen.trafficgenhelper import TRAFFIC_DEFAULTS
from conf import settings as S
from conf import get_test_param
@@ -37,7 +36,7 @@ class TestCase(object):
In this basic form runs RFC2544 throughput test
"""
- def __init__(self, cfg, results_dir, performance_test=True):
+ def __init__(self, cfg, results_dir):
"""Pull out fields from test config
:param cfg: A dictionary of string-value pairs describing the test
@@ -46,11 +45,19 @@ class TestCase(object):
:param results_dir: Where the csv formatted results are written.
"""
self._hugepages_mounted = False
+ self._traffic_ctl = None
+ self._vnf_ctl = None
+ self._vswitch_ctl = None
+ self._collector = None
+ self._loadgen = None
+ self._output_file = None
+ self._tc_results = None
# set test parameters; CLI options take precedence to testcase settings
self._logger = logging.getLogger(__name__)
self.name = cfg['Name']
self.desc = cfg.get('Description', 'No description given.')
+ self.test = cfg.get('TestSteps', None)
bidirectional = cfg.get('biDirectional', TRAFFIC_DEFAULTS['bidir'])
bidirectional = get_test_param('bidirectional', bidirectional)
@@ -63,7 +70,6 @@ class TestCase(object):
self.deployment = cfg['Deployment']
self._frame_mod = cfg.get('Frame Modification', None)
- self._performance_test = performance_test
self._tunnel_type = None
self._tunnel_operation = None
@@ -131,10 +137,8 @@ class TestCase(object):
# Packet Forwarding mode
self._vswitch_none = 'none' == S.getValue('VSWITCH').strip().lower()
- def run(self):
- """Run the test
-
- All setup and teardown through controllers is included.
+ def run_initialize(self):
+ """ Prepare test execution environment
"""
self._logger.debug(self.name)
@@ -163,35 +167,67 @@ class TestCase(object):
self._logger.debug("Controllers:")
loader = Loader()
- traffic_ctl = component_factory.create_traffic(
+ self._traffic_ctl = component_factory.create_traffic(
self._traffic['traffic_type'],
loader.get_trafficgen_class())
- vnf_ctl = component_factory.create_vnf(
+
+ self._vnf_ctl = component_factory.create_vnf(
self.deployment,
loader.get_vnf_class())
if self._vswitch_none:
- vswitch_ctl = component_factory.create_pktfwd(
+ self._vswitch_ctl = component_factory.create_pktfwd(
loader.get_pktfwd_class())
else:
- vswitch_ctl = component_factory.create_vswitch(
+ self._vswitch_ctl = component_factory.create_vswitch(
self.deployment,
loader.get_vswitch_class(),
self._traffic,
self._tunnel_operation)
- collector = component_factory.create_collector(
+ self._collector = component_factory.create_collector(
loader.get_collector_class(),
self._results_dir, self.name)
- loadgen = component_factory.create_loadgen(
+ self._loadgen = component_factory.create_loadgen(
self._loadgen,
self._load_cfg)
+ self._output_file = os.path.join(self._results_dir, "result_" + self.name +
+ "_" + self.deployment + ".csv")
+
self._logger.debug("Setup:")
- with vswitch_ctl, loadgen:
- with vnf_ctl, collector:
+
+ def run_finalize(self):
+ """ Tear down test execution environment and record test results
+ """
+ # umount hugepages if mounted
+ self._umount_hugepages()
+
+ def run_report(self):
+ """ Report test results
+ """
+ self._logger.debug("self._collector Results:")
+ self._collector.print_results()
+
+ if S.getValue('mode') != 'trafficgen-off':
+ self._logger.debug("Traffic Results:")
+ self._traffic_ctl.print_results()
+
+ self._tc_results = self._append_results(self._traffic_ctl.get_results())
+ TestCase._write_result_to_file(self._tc_results, self._output_file)
+
+ def run(self):
+ """Run the test
+
+ All setup and teardown through controllers is included.
+ """
+ # prepare test execution environment
+ self.run_initialize()
+
+ with self._vswitch_ctl, self._loadgen:
+ with self._vnf_ctl, self._collector:
if not self._vswitch_none:
- self._add_flows(vswitch_ctl)
+ self._add_flows()
# run traffic generator if requested, otherwise wait for manual termination
if S.getValue('mode') == 'trafficgen-off':
@@ -209,30 +245,18 @@ class TestCase(object):
print('Please respond with \'yes\' or \'y\' ', end='')
else:
break
- with traffic_ctl:
- traffic_ctl.send_traffic(self._traffic)
+ with self._traffic_ctl:
+ self._traffic_ctl.send_traffic(self._traffic)
# dump vswitch flows before they are affected by VNF termination
if not self._vswitch_none:
- vswitch_ctl.dump_vswitch_flows()
+ self._vswitch_ctl.dump_vswitch_flows()
- # umount hugepages if mounted
- self._umount_hugepages()
+ # tear down test execution environment and log results
+ self.run_finalize()
- self._logger.debug("Collector Results:")
- collector.print_results()
-
- if S.getValue('mode') != 'trafficgen-off':
- self._logger.debug("Traffic Results:")
- traffic_ctl.print_results()
-
- output_file = os.path.join(self._results_dir, "result_" + self.name +
- "_" + self.deployment + ".csv")
-
- tc_results = self._append_results(traffic_ctl.get_results())
- TestCase._write_result_to_file(tc_results, output_file)
-
- report.generate(output_file, tc_results, collector.get_results(), self._performance_test)
+ # report test results
+ self.run_report()
def _append_results(self, results):
"""
@@ -335,7 +359,6 @@ class TestCase(object):
for result in results:
writer.writerow(result)
-
@staticmethod
def _get_unique_keys(list_of_dicts):
"""Gets unique key values as ordered list of strings in given dicts
@@ -351,13 +374,10 @@ class TestCase(object):
return list(result.keys())
-
- def _add_flows(self, vswitch_ctl):
+ def _add_flows(self):
"""Add flows to the vswitch
-
- :param vswitch_ctl vswitch controller
"""
- vswitch = vswitch_ctl.get_vswitch()
+ vswitch = self._vswitch_ctl.get_vswitch()
# TODO BOM 15-08-07 the frame mod code assumes that the
# physical ports are ports 1 & 2. The actual numbers
# need to be retrived from the vSwitch and the metadata value
diff --git a/tools/report/report.py b/tools/report/report.py
index 92463f26..7d991011 100644
--- a/tools/report/report.py
+++ b/tools/report/report.py
@@ -70,13 +70,21 @@ def _get_env(result):
return env
-def generate(input_file, tc_results, tc_stats, performance_test=True):
+def generate(input_file, tc_results, tc_stats, test_type='performance'):
"""Generate actual report.
- Generate a Markdown-formatted file using results of tests and some
+ Generate a Markdown and RST formatted files using results of tests and some
parsed system info.
:param input_file: Path to CSV results file
+ :param tc_results: A list of dictionaries with detailed test results.
+ Each dictionary represents test results for one of specified packet
+ sizes.
+ :param tc_stats: System statistics collected during testcase execution.
+ These statistics are overall statistics for all specified packet
+ sizes.
+ :test_type: Specifies type of the testcase. Supported values are
+ 'performance' and 'integration'.
:returns: Path to generated report
"""
@@ -89,16 +97,18 @@ def generate(input_file, tc_results, tc_stats, performance_test=True):
try:
for result in tc_results:
test_config = {}
- if performance_test:
+ if test_type == 'performance':
for tc_conf in S.getValue('PERFORMANCE_TESTS'):
if tc_conf['Name'] == result[ResultsConstants.ID]:
test_config = tc_conf
break
- else:
+ elif test_type == 'integration':
for tc_conf in S.getValue('INTEGRATION_TESTS'):
if tc_conf['Name'] == result[ResultsConstants.ID]:
test_config = tc_conf
break
+ else:
+ logging.error("Unsupported test type '%s'. Test details are not known.", test_type)
# pass test results, env details and configuration to template
tests.append({
diff --git a/vsperf b/vsperf
index 53f55075..35283185 100755
--- a/vsperf
+++ b/vsperf
@@ -36,7 +36,8 @@ sys.dont_write_bytecode = True
from conf import settings
from conf import get_test_param
from core.loader import Loader
-from testcases import TestCase
+from testcases import PerformanceTestCase
+from testcases import IntegrationTestCase
from tools import tasks
from tools.pkt_gen import trafficgen
from tools.opnfvdashboard import opnfvdashboard
@@ -156,7 +157,7 @@ def parse_arguments():
name contains RFC2544 less those containing "p2p"')
group.add_argument('--verbosity', choices=list_logging_levels(),
help='debug level')
- group.add_argument('--run-integration', action='store_true', help='run integration tests')
+ group.add_argument('--integration', action='store_true', help='execute integration tests')
group.add_argument('--trafficgen', help='traffic generator to use')
group.add_argument('--vswitch', help='vswitch implementation to use')
group.add_argument('--fwdapp', help='packet forwarding application to use')
@@ -343,11 +344,8 @@ def main():
settings.load_from_dir('conf')
- performance_test = True
-
# Load non performance/integration tests
- if args['run_integration']:
- performance_test = False
+ if args['integration']:
settings.load_from_dir('conf/integration')
# load command line parameters first in case there are settings files
@@ -472,14 +470,18 @@ def main():
traffic_ctl.print_results()
else:
# configure tests
- testcases = settings.getValue('PERFORMANCE_TESTS')
- if args['run_integration']:
+ if args['integration']:
testcases = settings.getValue('INTEGRATION_TESTS')
+ else:
+ testcases = settings.getValue('PERFORMANCE_TESTS')
all_tests = []
for cfg in testcases:
try:
- all_tests.append(TestCase(cfg, results_path, performance_test))
+ if args['integration']:
+ all_tests.append(IntegrationTestCase(cfg, results_path))
+ else:
+ all_tests.append(PerformanceTestCase(cfg, results_path))
except (Exception) as _:
logger.exception("Failed to create test: %s",
cfg.get('Name', '<Name not set>'))
@@ -489,9 +491,9 @@ def main():
if args['list']:
print("Available Tests:")
- print("======")
+ print("================")
for test in all_tests:
- print('* %-18s%s' % ('%s:' % test.name, test.desc))
+ print('* %-30s %s' % ('%s:' % test.name, test.desc))
exit()
if args['list_trafficgens']:
diff --git a/vswitches/ovs.py b/vswitches/ovs.py
new file mode 100644
index 00000000..06dc7a1a
--- /dev/null
+++ b/vswitches/ovs.py
@@ -0,0 +1,229 @@
+# Copyright 2015-2016 Intel Corporation.
+#
+# 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.
+
+"""VSPERF Open vSwitch base class
+"""
+
+import logging
+import re
+from conf import settings
+from vswitches.vswitch import IVSwitch
+from src.ovs import OFBridge, flow_key, flow_match
+
+_VSWITCHD_CONST_ARGS = ['--', '--pidfile', '--log-file']
+
+class IVSwitchOvs(IVSwitch):
+ """Open vSwitch base class implementation
+
+ The method docstrings document only considerations specific to this
+ implementation. For generic information of the nature of the methods,
+ see the interface.
+ """
+
+ def __init__(self):
+ """See IVswitch for general description
+ """
+ self._vswitchd = None
+ self._logger = logging.getLogger(__name__)
+ self._bridges = {}
+ self._vswitchd_args = _VSWITCHD_CONST_ARGS
+
+ def start(self):
+ """See IVswitch for general description
+ """
+ self._logger.info("Starting vswitchd...")
+ self._vswitchd.start()
+ self._logger.info("Vswitchd...Started.")
+
+ def stop(self):
+ """See IVswitch for general description
+ """
+ self._logger.info("Terminating vswitchd...")
+ self._vswitchd.kill()
+ self._logger.info("Vswitchd...Terminated.")
+
+ def add_switch(self, switch_name, params=None):
+ """See IVswitch for general description
+ """
+ bridge = OFBridge(switch_name)
+ bridge.create(params)
+ bridge.set_db_attribute('Open_vSwitch', '.',
+ 'other_config:max-idle',
+ settings.getValue('VSWITCH_FLOW_TIMEOUT'))
+ self._bridges[switch_name] = bridge
+
+ def del_switch(self, switch_name):
+ """See IVswitch for general description
+ """
+ bridge = self._bridges[switch_name]
+ self._bridges.pop(switch_name)
+ bridge.destroy()
+
+ def add_phy_port(self, switch_name):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError
+
+ def add_vport(self, switch_name):
+ """See IVswitch for general description
+ """
+ raise NotImplementedError
+
+ def add_tunnel_port(self, switch_name, remote_ip, tunnel_type='vxlan', params=None):
+ """Creates tunneling port
+ """
+ bridge = self._bridges[switch_name]
+ pcount = str(self._get_port_count('type=' + tunnel_type))
+ port_name = tunnel_type + pcount
+ local_params = ['--', 'set', 'Interface', port_name,
+ 'type=' + tunnel_type,
+ 'options:remote_ip=' + remote_ip]
+
+ if params is not None:
+ local_params = local_params + params
+
+ of_port = bridge.add_port(port_name, local_params)
+ return (port_name, of_port)
+
+ def get_ports(self, switch_name):
+ """See IVswitch for general description
+ """
+ bridge = self._bridges[switch_name]
+ ports = list(bridge.get_ports().items())
+ return [(name, of_port) for (name, (of_port, _)) in ports]
+
+ def del_port(self, switch_name, port_name):
+ """See IVswitch for general description
+ """
+ bridge = self._bridges[switch_name]
+ bridge.del_port(port_name)
+
+ def add_flow(self, switch_name, flow, cache='off'):
+ """See IVswitch for general description
+ """
+ bridge = self._bridges[switch_name]
+ bridge.add_flow(flow, cache=cache)
+
+ def del_flow(self, switch_name, flow=None):
+ """See IVswitch for general description
+ """
+ flow = flow or {}
+ bridge = self._bridges[switch_name]
+ bridge.del_flow(flow)
+
+ def dump_flows(self, switch_name):
+ """See IVswitch for general description
+ """
+ bridge = self._bridges[switch_name]
+ bridge.dump_flows()
+
+ def add_route(self, switch_name, network, destination):
+ """See IVswitch for general description
+ """
+ bridge = self._bridges[switch_name]
+ bridge.add_route(network, destination)
+
+ def set_tunnel_arp(self, ip_addr, mac_addr, switch_name):
+ """See IVswitch for general description
+ """
+ bridge = self._bridges[switch_name]
+ bridge.set_tunnel_arp(ip_addr, mac_addr, switch_name)
+
+ def _get_port_count(self, param):
+ """Returns the number of ports having a certain parameter
+ """
+ cnt = 0
+ for k in self._bridges:
+ pparams = [c for (_, (_, c)) in list(self._bridges[k].get_ports().items())]
+ phits = [i for i in pparams if param in i]
+ cnt += len(phits)
+
+ if cnt is None:
+ cnt = 0
+ return cnt
+
+ def validate_add_switch(self, result, switch_name, params=None):
+ """Validate - Create a new logical switch with no ports
+ """
+ bridge = self._bridges[switch_name]
+ output = bridge.run_vsctl(['show'], check_error=True)
+ assert not output[1] # there shouldn't be any stderr, but in case
+ assert re.search('Bridge ["\']?%s["\']?' % switch_name, output[0]) is not None
+ return True
+
+ def validate_del_switch(self, result, switch_name):
+ """Validate removal of switch
+ """
+ bridge = OFBridge('tmp')
+ output = bridge.run_vsctl(['show'], check_error=True)
+ assert not output[1] # there shouldn't be any stderr, but in case
+ assert re.search('Bridge ["\']?%s["\']?' % switch_name, output[0]) is None
+ return True
+
+ def validate_add_phy_port(self, result, switch_name):
+ """ Validate that physical port was added to bridge.
+ """
+ bridge = self._bridges[switch_name]
+ output = bridge.run_vsctl(['show'], check_error=True)
+ assert not output[1] # there shouldn't be any stderr, but in case
+ assert re.search('Port ["\']?%s["\']?' % result[0], output[0]) is not None
+ assert re.search('Interface ["\']?%s["\']?' % result[0], output[0]) is not None
+ return True
+
+ def validate_add_vport(self, result, switch_name):
+ """ Validate that virtual port was added to bridge.
+ """
+ return self.validate_add_phy_port(result, switch_name)
+
+ def validate_del_port(self, result, switch_name, port_name):
+ """ Validate that port_name was removed from bridge.
+ """
+ bridge = self._bridges[switch_name]
+ output = bridge.run_vsctl(['show'], check_error=True)
+ assert not output[1] # there shouldn't be any stderr, but in case
+ assert 'Port "%s"' % port_name not in output[0]
+ return True
+
+ def validate_add_flow(self, result, switch_name, flow, cache='off'):
+ """ Validate insertion of the flow into the switch
+ """
+ if 'idle_timeout' in flow:
+ del(flow['idle_timeout'])
+
+ # Note: it should be possible to call `ovs-ofctl dump-flows switch flow`
+ # to verify flow insertion, but it doesn't accept the same flow syntax
+ # as add-flow, so we have to compare it the hard way
+
+ # get dump of flows and compare them one by one
+ flow_src = flow_key(flow)
+ bridge = self._bridges[switch_name]
+ output = bridge.run_ofctl(['dump-flows', switch_name], check_error=True)
+ for flow_dump in output[0].split('\n'):
+ if flow_match(flow_dump, flow_src):
+ # flow was added correctly
+ return True
+ return False
+
+ def validate_del_flow(self, result, switch_name, flow=None):
+ """ Validate removal of the flow
+ """
+ if not flow:
+ # what else we can do?
+ return True
+ return not self.validate_add_flow(result, switch_name, flow)
+
+ def validate_dump_flows(self, result, switch_name):
+ """ Validate call of flow dump
+ """
+ return True
diff --git a/vswitches/ovs_dpdk_vhost.py b/vswitches/ovs_dpdk_vhost.py
index 2ace64a2..9d29c9d1 100644
--- a/vswitches/ovs_dpdk_vhost.py
+++ b/vswitches/ovs_dpdk_vhost.py
@@ -17,13 +17,11 @@
import logging
from conf import settings
-from vswitches.vswitch import IVSwitch
-from src.ovs import VSwitchd, OFBridge
+from vswitches.ovs import IVSwitchOvs
+from src.ovs import VSwitchd
from src.dpdk import dpdk
-_VSWITCHD_CONST_ARGS = ['--', '--pidfile', '--log-file']
-
-class OvsDpdkVhost(IVSwitch):
+class OvsDpdkVhost(IVSwitchOvs):
""" Open vSwitch with DPDK support
Generic OVS wrapper functionality in src.ovs is maximally used. This
@@ -35,21 +33,19 @@ class OvsDpdkVhost(IVSwitch):
see the interface.
"""
- _logger = logging.getLogger()
-
def __init__(self):
- vswitchd_args = ['--dpdk']
- vswitchd_args += settings.getValue('VSWITCHD_DPDK_ARGS')
- vswitchd_args += _VSWITCHD_CONST_ARGS
+ super(OvsDpdkVhost, self).__init__()
+ self._logger = logging.getLogger(__name__)
+ self._vswitchd_args = ['--dpdk']
+ self._vswitchd_args += settings.getValue('VSWITCHD_DPDK_ARGS')
if settings.getValue('VNF').endswith('Cuse'):
self._logger.info("Inserting VHOST Cuse modules into kernel...")
dpdk.insert_vhost_modules()
- self._vswitchd = VSwitchd(vswitchd_args=vswitchd_args,
+ self._vswitchd = VSwitchd(vswitchd_args=self._vswitchd_args,
expected_cmd=
r'EAL: Master l*core \d+ is ready')
- self._bridges = {}
def start(self):
"""See IVswitch for general description
@@ -57,47 +53,32 @@ class OvsDpdkVhost(IVSwitch):
Activates DPDK kernel modules, ovsdb and vswitchd.
"""
dpdk.init()
- self._vswitchd.start()
+ super(OvsDpdkVhost, self).start()
def stop(self):
"""See IVswitch for general description
Kills ovsdb and vswitchd and removes DPDK kernel modules.
"""
- self._vswitchd.kill()
+ super(OvsDpdkVhost, self).stop()
dpdk.cleanup()
dpdk.remove_vhost_modules()
def add_switch(self, switch_name, params=None):
"""See IVswitch for general description
"""
- bridge = OFBridge(switch_name)
- if params is None:
- bridge.create(['--', 'set', 'bridge', switch_name,
- 'datapath_type=netdev'])
- else:
- bridge.create(['--', 'set', 'bridge', switch_name,
- 'datapath_type=netdev'] + params)
+ switch_params = ['--', 'set', 'bridge', switch_name, 'datapath_type=netdev']
+ if params:
+ switch_params = switch_params + params
- bridge.set_db_attribute('Open_vSwitch', '.',
- 'other_config:max-idle',
- settings.getValue('VSWITCH_FLOW_TIMEOUT'))
+ super(OvsDpdkVhost, self).add_switch(switch_name, switch_params)
if settings.getValue('VSWITCH_AFFINITIZATION_ON') == 1:
# Sets the PMD core mask to VSWITCH_PMD_CPU_MASK
# for CPU core affinitization
- bridge.set_db_attribute('Open_vSwitch', '.',
- 'other_config:pmd-cpu-mask',
- settings.getValue('VSWITCH_PMD_CPU_MASK'))
-
- self._bridges[switch_name] = bridge
-
- def del_switch(self, switch_name):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- self._bridges.pop(switch_name)
- bridge.destroy()
+ self._bridges[switch_name].set_db_attribute('Open_vSwitch', '.',
+ 'other_config:pmd-cpu-mask',
+ settings.getValue('VSWITCH_PMD_CPU_MASK'))
def add_phy_port(self, switch_name):
"""See IVswitch for general description
@@ -134,80 +115,3 @@ class OvsDpdkVhost(IVSwitch):
of_port = bridge.add_port(port_name, params)
return (port_name, of_port)
-
- def add_tunnel_port(self, switch_name, remote_ip, tunnel_type='vxlan', params=None):
- """Creates tunneling port
- """
- bridge = self._bridges[switch_name]
- pcount = str(self._get_port_count('type=' + tunnel_type))
- port_name = tunnel_type + pcount
- local_params = ['--', 'set', 'Interface', port_name,
- 'type=' + tunnel_type,
- 'options:remote_ip=' + remote_ip]
-
- if params is not None:
- local_params = local_params + params
-
- of_port = bridge.add_port(port_name, local_params)
- return (port_name, of_port)
-
- def get_ports(self, switch_name):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- ports = list(bridge.get_ports().items())
- return [(name, of_port) for (name, (of_port, _)) in ports]
-
- def del_port(self, switch_name, port_name):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- bridge.del_port(port_name)
-
- def add_flow(self, switch_name, flow, cache='off'):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- bridge.add_flow(flow, cache=cache)
-
- def del_flow(self, switch_name, flow=None):
- """See IVswitch for general description
- """
- flow = flow or {}
- bridge = self._bridges[switch_name]
- bridge.del_flow(flow)
-
- def dump_flows(self, switch_name):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- bridge.dump_flows()
-
- def add_route(self, switch_name, network, destination):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- bridge.add_route(network, destination)
-
- def set_tunnel_arp(self, ip_addr, mac_addr, switch_name):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- bridge.set_tunnel_arp(ip_addr, mac_addr, switch_name)
-
- def _get_port_count(self, param):
- """Returns the number of ports having a certain parameter
-
- :param bridge: The src.ovs.ofctl.OFBridge on which to operate
- :param param: The parameter to search for
- :returns: Count of matches
- """
- cnt = 0
- for k in self._bridges:
- pparams = [c for (_, (_, c)) in list(self._bridges[k].get_ports().items())]
- phits = [i for i in pparams if param in i]
- cnt += len(phits)
-
- if cnt is None:
- cnt = 0
- return cnt
diff --git a/vswitches/ovs_vanilla.py b/vswitches/ovs_vanilla.py
index 078d7006..6a380b1b 100644
--- a/vswitches/ovs_vanilla.py
+++ b/vswitches/ovs_vanilla.py
@@ -17,15 +17,12 @@
import logging
from conf import settings
-from vswitches.vswitch import IVSwitch
-from src.ovs import VSwitchd, OFBridge, DPCtl
+from vswitches.ovs import IVSwitchOvs
+from src.ovs import VSwitchd, DPCtl
from tools.module_manager import ModuleManager
from tools import tasks
-_LOGGER = logging.getLogger(__name__)
-VSWITCHD_CONST_ARGS = ['--', '--log-file']
-
-class OvsVanilla(IVSwitch):
+class OvsVanilla(IVSwitchOvs):
""" Open vSwitch
This is wrapper for functionality implemented in src.ovs.
@@ -35,16 +32,16 @@ class OvsVanilla(IVSwitch):
see the interface definition.
"""
- _logger = logging.getLogger()
_ports = settings.getValue('VSWITCH_VANILLA_PHY_PORT_NAMES')
_current_id = 0
_vport_id = 0
def __init__(self):
- #vswitchd_args = VSWITCHD_CONST_ARGS
- vswitchd_args = ["unix:%s" % VSwitchd.get_db_sock_path()]
- vswitchd_args += settings.getValue('VSWITCHD_VANILLA_ARGS')
- self._vswitchd = VSwitchd(vswitchd_args=vswitchd_args,
+ super(OvsVanilla, self).__init__()
+ self._logger = logging.getLogger(__name__)
+ self._vswitchd_args = ["unix:%s" % VSwitchd.get_db_sock_path()]
+ self._vswitchd_args += settings.getValue('VSWITCHD_VANILLA_ARGS')
+ self._vswitchd = VSwitchd(vswitchd_args=self._vswitchd_args,
expected_cmd="db.sock: connected")
self._bridges = {}
self._module_manager = ModuleManager()
@@ -56,9 +53,7 @@ class OvsVanilla(IVSwitch):
"""
self._module_manager.insert_modules(
settings.getValue('VSWITCH_VANILLA_KERNEL_MODULES'))
- self._logger.info("Starting Vswitchd...")
- self._vswitchd.start()
- self._logger.info("Vswitchd...Started.")
+ super(OvsVanilla, self).start()
def stop(self):
"""See IVswitch for general description
@@ -70,32 +65,16 @@ class OvsVanilla(IVSwitch):
tapx = 'tap' + str(i)
tasks.run_task(['sudo', 'ip', 'tuntap', 'del',
tapx, 'mode', 'tap'],
- _LOGGER, 'Deleting ' + tapx, False)
+ self._logger, 'Deleting ' + tapx, False)
self._vport_id = 0
- self._vswitchd.kill()
+ super(OvsVanilla, self).stop()
dpctl = DPCtl()
dpctl.del_dp()
self._module_manager.remove_modules()
- def add_switch(self, switch_name, params=None):
- """See IVswitch for general description
- """
- bridge = OFBridge(switch_name)
- bridge.create(params)
- bridge.set_db_attribute('Open_vSwitch', '.',
- 'other_config:max-idle', '60000')
- self._bridges[switch_name] = bridge
-
- def del_switch(self, switch_name):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- self._bridges.pop(switch_name)
- bridge.destroy()
-
def add_phy_port(self, switch_name):
"""
Method adds port based on configured VSWITCH_VANILLA_PHY_PORT_NAMES
@@ -119,7 +98,7 @@ class OvsVanilla(IVSwitch):
# For PVP only
tasks.run_task(['sudo', 'ifconfig', port_name, '0'],
- _LOGGER, 'Remove IP', False)
+ self._logger, 'Remove IP', False)
of_port = bridge.add_port(port_name, params)
self._current_id += 1
@@ -137,95 +116,17 @@ class OvsVanilla(IVSwitch):
tasks.run_task(['sudo', 'ip', 'tuntap', 'del',
tap_name, 'mode', 'tap'],
- _LOGGER, 'Creating tap device...', False)
+ self._logger, 'Creating tap device...', False)
tasks.run_task(['sudo', 'ip', 'tuntap', 'add',
tap_name, 'mode', 'tap'],
- _LOGGER, 'Creating tap device...', False)
+ self._logger, 'Creating tap device...', False)
tasks.run_task(['sudo', 'ifconfig', tap_name, '0'],
- _LOGGER, 'Bring up ' + tap_name, False)
+ self._logger, 'Bring up ' + tap_name, False)
bridge = self._bridges[switch_name]
of_port = bridge.add_port(tap_name, [])
return (tap_name, of_port)
- def add_tunnel_port(self, switch_name, remote_ip, tunnel_type='vxlan',
- params=None):
- """Creates tunneling port
- """
- bridge = self._bridges[switch_name]
- pcount = str(self._get_port_count('type=' + tunnel_type))
- port_name = tunnel_type + pcount
- local_params = ['--', 'set', 'Interface', port_name,
- 'type=' + tunnel_type,
- 'options:remote_ip=' + remote_ip]
-
- if params is not None:
- local_params = local_params + params
-
- of_port = bridge.add_port(port_name, local_params)
- return (port_name, of_port)
-
- def get_ports(self, switch_name):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- ports = list(bridge.get_ports().items())
- return [(name, of_port) for (name, (of_port, _)) in ports]
-
- def del_port(self, switch_name, port_name):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- bridge.del_port(port_name)
-
- def add_flow(self, switch_name, flow, cache='off'):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- bridge.add_flow(flow, cache=cache)
-
- def del_flow(self, switch_name, flow=None):
- """See IVswitch for general description
- """
- flow = flow or {}
- bridge = self._bridges[switch_name]
- bridge.del_flow(flow)
-
- def dump_flows(self, switch_name):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- bridge.dump_flows()
-
- def add_route(self, switch_name, network, destination):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- bridge.add_route(network, destination)
-
- def set_tunnel_arp(self, ip_addr, mac_addr, switch_name):
- """See IVswitch for general description
- """
- bridge = self._bridges[switch_name]
- bridge.set_tunnel_arp(ip_addr, mac_addr, switch_name)
-
- def _get_port_count(self, param):
- """Returns the number of ports having a certain parameter
-
- :param bridge: The src.ovs.ofctl.OFBridge on which to operate
- :param param: The parameter to search for
- :returns: Count of matches
- """
- cnt = 0
- for k in self._bridges:
- pparams = [c for (_, (_, c)) in list(self._bridges[k].get_ports().items())]
- phits = [i for i in pparams if param in i]
- cnt += len(phits)
-
- if cnt is None:
- cnt = 0
- return cnt
-