summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Klozik <martinx.klozik@intel.com>2017-12-19 08:58:43 +0000
committerMartin Klozik <martinx.klozik@intel.com>2018-01-11 11:28:02 +0000
commita2d97b05fb3b7ff62f62bcb16d36c51e3df6ca77 (patch)
tree8cb4e2cd7a463275416d8ac2422b6907166f2a94
parent58d53561bad01ae7829fdeee0c67f5dae4a9dc34 (diff)
capture: Traffic capture examples
Traffic can be captured also at DUT side. Two options are supported by vsperf: 1) Traffic is captured inside VM after it has been processed by vSwitch. This can be used for verification of vSwitch frame modification functionality, including HW offloading at ingress side. 2) Another NIC under the test (NIC2) is added into standard VSPERF DUT setup. Traffic is then forwarded from TGen through NIC and vSwitch to NIC2 and then over patch cable back to NIC2, the vSwitch, NIC and to the traffic generator. This setup supports also verification of HW offloading at egress side of NIC2 and thus it can be used for validation of smart NICS. Both options above are traffic generator agnostic if compared to direct support of traffic capture by traffic generator. This patch introduces example testcases for both options. Detailed documentation will be pushed as a standalone patch. JIRA: VSPERF-556 Change-Id: I23e12e45768ae4dbe9442f74d8391c3d5b2c7895 Signed-off-by: Martin Klozik <martinx.klozik@intel.com> Reviewed-by: Richard Elias <richardx.elias@intel.com> Reviewed-by: Al Morton <acmorton@att.com> Reviewed-by: Christian Trautman <ctrautma@redhat.com> Reviewed-by: Sridhar Rao <sridhar.rao@spirent.com> Reviewed-by: Trevor Cooper <trevor.cooper@intel.com>
-rw-r--r--conf/__init__.py13
-rw-r--r--conf/integration/01_testcases.conf121
-rw-r--r--docs/testing/user/userguide/teststeps.rst4
-rw-r--r--testcases/testcase.py3
-rw-r--r--tools/tasks.py33
-rw-r--r--tools/teststepstools.py17
6 files changed, 187 insertions, 4 deletions
diff --git a/conf/__init__.py b/conf/__init__.py
index a7c0ee5d..d5d26757 100644
--- a/conf/__init__.py
+++ b/conf/__init__.py
@@ -124,6 +124,13 @@ class Settings(object):
if name is not None and value is not None:
super(Settings, self).__setattr__(name, value)
+ def resetValue(self, attr):
+ """If parameter was overridden by TEST_PARAMS, then it will
+ be set to its original value.
+ """
+ if attr in self.__dict__['TEST_PARAMS']:
+ self.__dict__['TEST_PARAMS'].pop(attr)
+
def load_from_file(self, path):
"""Update ``settings`` with values found in module at ``path``.
"""
@@ -324,6 +331,12 @@ class Settings(object):
assert value == self.__dict__[name]
return True
+ def validate_resetValue(self, dummy_result, attr):
+ """Verifies, that value was correctly reset
+ """
+ return 'TEST_PARAMS' not in self.__dict__ or \
+ attr not in self.__dict__['TEST_PARAMS']
+
settings = Settings()
def get_test_param(key, default=None):
diff --git a/conf/integration/01_testcases.conf b/conf/integration/01_testcases.conf
index dfc8a4c2..692f1561 100644
--- a/conf/integration/01_testcases.conf
+++ b/conf/integration/01_testcases.conf
@@ -1000,6 +1000,127 @@ INTEGRATION_TESTS = [
# END of VPP tests used by VERIFY and MERGE jobs by OPNFV Jenkins
#
+ #
+ # Examples of functional testcases with traffic capture validation
+ #
+ # Capture Example 1 - Traffic capture inside VM (PVP scenario)
+ # This TestCase will modify VLAN ID set by the traffic generator to the new value.
+ # Correct VLAN ID settings is verified by inspection of captured frames.
+ {
+ "Name": "capture_pvp_modify_vid",
+ "Deployment": "pvp",
+ "Description": "Test and verify VLAN ID modification by Open vSwitch",
+ "Parameters" : {
+ "VSWITCH" : "OvsDpdkVhost", # works also for Vanilla OVS
+ "TRAFFICGEN_DURATION" : 5,
+ "TRAFFIC" : {
+ "traffic_type" : "rfc2544_continuous",
+ "frame_rate" : 100,
+ 'vlan': {
+ 'enabled': True,
+ 'id': 8,
+ 'priority': 1,
+ 'cfi': 0,
+ },
+ },
+ "GUEST_LOOPBACK" : ['linux_bridge'],
+ },
+ "TestSteps": [
+ # replace original flows with vlan ID modification
+ ['!vswitch', 'add_flow', 'br0', {'in_port': '1', 'actions': ['mod_vlan_vid:4','output:3']}],
+ ['!vswitch', 'add_flow', 'br0', {'in_port': '2', 'actions': ['mod_vlan_vid:4','output:4']}],
+ ['vswitch', 'dump_flows', 'br0'],
+ # verify that received frames have modified vlan ID
+ ['VNF0', 'execute_and_wait', 'tcpdump -i eth0 -c 5 -w dump.pcap vlan 4 &'],
+ ['trafficgen', 'send_traffic',{}],
+ ['!VNF0', 'execute_and_wait', 'tcpdump -qer dump.pcap vlan 4 2>/dev/null | wc -l','|^(\d+)$'],
+ ['tools', 'assert', '#STEP[-1][0] == 5'],
+ ],
+ },
+]
+# Capture Example 2 - Setup with 2 NICs, where traffic is captured after it is
+# processed by NIC under the test (2nd NIC). See documentation for further details.
+# This TestCase will strip VLAN headers from traffic sent by the traffic generator.
+# The removal of VLAN headers is verified by inspection of captured frames.
+#
+# NOTE: This setup expects a DUT with two NICs with two ports each. First NIC is
+# connected to the traffic generator (standard VSPERF setup). Ports of a second NIC
+# are interconnected by a patch cable. PCI addresses of all four ports have to be
+# properly configured in the WHITELIST_NICS parameter.
+_CAPTURE_P2P2P_OVS_ACTION = ''
+_CAPTURE_P2P2P_SETUP = [
+ # restore original NICS configuration, so we can refer to NICS elements
+ ['settings', 'resetValue', 'WHITELIST_NICS'],
+ ['settings', 'resetValue', 'NICS'],
+ # create and configure two bridges to forward traffic through NIC under
+ # the test and back to the traffic generator
+ # 1st bridge:
+ ['vswitch', 'add_switch', 'br0'],
+ ['tools', 'exec_shell', 'sudo ip addr flush dev $NICS[0]["device"]'],
+ ['tools', 'exec_shell', 'sudo ip link set dev $NICS[0]["device"] up'],
+ ['tools', 'exec_shell', '$TOOLS["ovs-vsctl"] add-port br0 $NICS[0]["device"]'],
+ ['tools', 'exec_shell', 'sudo $TOOLS["bind-tool"] --bind igb_uio $NICS[3]["pci"]'],
+ ['tools', 'exec_shell', '$TOOLS["ovs-vsctl"] add-port br0 dpdk0 -- '
+ 'set Interface dpdk0 type=dpdk options:dpdk-devargs=$NICS[3]["pci"]'],
+ ['tools', 'exec_shell', '$TOOLS["ovs-ofctl"] add-flow br0 in_port=1,action='
+ '$_CAPTURE_P2P2P_OVS_ACTION,output:2'],
+ # 2nd bridge:
+ ['vswitch', 'add_switch', 'br1'],
+ ['tools', 'exec_shell', 'sudo ip addr flush dev $NICS[2]["device"]'],
+ ['tools', 'exec_shell', 'sudo ip link set dev $NICS[2]["device"] up'],
+ ['tools', 'exec_shell', '$TOOLS["ovs-vsctl"] add-port br1 $NICS[2]["device"]'],
+ ['tools', 'exec_shell', 'sudo ip addr flush dev $NICS[1]["device"]'],
+ ['tools', 'exec_shell', 'sudo ip link set dev $NICS[1]["device"] up'],
+ ['tools', 'exec_shell', '$TOOLS["ovs-vsctl"] add-port br1 $NICS[1]["device"]'],
+ ['vswitch', 'add_flow', 'br1', {'in_port': '1', 'actions': ['output:2']}],
+ # log flow details
+ ['vswitch', 'dump_flows', 'br0'],
+ ['vswitch', 'dump_flows', 'br1'],
+]
+INTEGRATION_TESTS += [
+ {
+ "Name": "capture_p2p2p_strip_vlan_ovs",
+ "Deployment": "clean",
+ "Description": "P2P Continuous Stream",
+ "Parameters" : {
+ "_CAPTURE_P2P2P_OVS_ACTION" : 'strip_vlan',
+ "TRAFFIC" : {
+ "bidir" : "False",
+ "traffic_type" : "rfc2544_continuous",
+ "frame_rate" : 100,
+ 'l2': {
+ 'srcmac': "ca:fe:00:00:00:00",
+ 'dstmac': "00:00:00:00:00:01"
+ },
+ 'vlan': {
+ 'enabled': True,
+ 'id': 8,
+ 'priority': 1,
+ 'cfi': 0,
+ },
+ },
+ # suppress DPDK configuration, so physical interfaces are not bound to DPDK driver
+ 'WHITELIST_NICS' : [],
+ 'NICS' : [],
+ },
+ "TestSteps": _CAPTURE_P2P2P_SETUP + [
+ # capture traffic after processing by NIC under the test (after possible egress HW offloading)
+ ['tools', 'exec_shell_background', 'tcpdump -i $NICS[2]["device"] -c 5 -w capture.pcap '
+ 'ether src $TRAFFIC["l2"]["srcmac"]'],
+ ['trafficgen', 'send_traffic', {}],
+ ['vswitch', 'dump_flows', 'br0'],
+ ['vswitch', 'dump_flows', 'br1'],
+ # there must be 5 captured frames...
+ ['tools', 'exec_shell', 'tcpdump -r capture.pcap | wc -l', '|^(\d+)$'],
+ ['tools', 'assert', '#STEP[-1][0] == 5'],
+ # ...but no vlan headers
+ ['tools', 'exec_shell', 'tcpdump -r capture.pcap vlan | wc -l', '|^(\d+)$'],
+ ['tools', 'assert', '#STEP[-1][0] == 0'],
+ ],
+ },
+ #
+ # End of examples of functional testcases with traffic capture validation
+ #
]
# Example of TC definition with exact vSwitch, VNF and TRAFFICGEN values.
diff --git a/docs/testing/user/userguide/teststeps.rst b/docs/testing/user/userguide/teststeps.rst
index 40cc732c..08c95311 100644
--- a/docs/testing/user/userguide/teststeps.rst
+++ b/docs/testing/user/userguide/teststeps.rst
@@ -271,7 +271,9 @@ of supported objects and their most common functions follows:
in case that condition is not ``True``
* ``Eval expression`` - evaluates given expression as a python code and returns
its result
- * ``Exec_Shell command`` - executes a shell command
+ * ``Exec_Shell command`` - executes a shell command and wait until it finishes
+ * ``Exec_Shell_Background command`` - executes a shell command at background;
+ Command will be automatically terminated at the end of testcase execution.
* ``Exec_Python code`` - executes a python code
diff --git a/testcases/testcase.py b/testcases/testcase.py
index cf71d596..991c2890 100644
--- a/testcases/testcase.py
+++ b/testcases/testcase.py
@@ -265,6 +265,9 @@ class TestCase(object):
# Stop all VNFs started by TestSteps in case that something went wrong
self.step_stop_vnfs()
+ # Stop all processes executed by testcase
+ tasks.terminate_all_tasks(self._logger)
+
# umount hugepages if mounted
self._umount_hugepages()
diff --git a/tools/tasks.py b/tools/tasks.py
index 4179291f..18f4d712 100644
--- a/tools/tasks.py
+++ b/tools/tasks.py
@@ -114,6 +114,16 @@ def run_task(cmd, logger, msg=None, check_error=False):
return ('\n'.join(sout.decode(my_encoding).strip() for sout in stdout),
('\n'.join(sout.decode(my_encoding).strip() for sout in stderr)))
+def update_pids(pid):
+ """update list of running pids, so they can be terminated at the end
+ """
+ try:
+ pids = settings.getValue('_EXECUTED_PIDS')
+ pids.append(pid)
+ except AttributeError:
+ pids = [pid]
+ settings.setValue('_EXECUTED_PIDS', pids)
+
def run_background_task(cmd, logger, msg):
"""Run task in background and log when started.
@@ -132,6 +142,8 @@ def run_background_task(cmd, logger, msg):
proc = subprocess.Popen(map(os.path.expanduser, cmd), stdout=_get_stdout(), bufsize=0)
+ update_pids(proc.pid)
+
return proc.pid
@@ -174,14 +186,13 @@ def terminate_task_subtree(pid, signal='-15', sleep=10, logger=None):
:param logger: Logger to write details to
"""
try:
- output = subprocess.check_output("pgrep -P " + str(pid), shell=True).decode().rstrip('\n')
+ children = subprocess.check_output("pgrep -P " + str(pid), shell=True).decode().rstrip('\n').split()
except subprocess.CalledProcessError:
- output = ""
+ children = []
terminate_task(pid, signal, sleep, logger)
# just for case children were kept alive
- children = output.split('\n')
for child in children:
terminate_task(child, signal, sleep, logger)
@@ -208,6 +219,22 @@ def terminate_task(pid, signal='-15', sleep=10, logger=None):
if signal.lstrip('-').upper() not in ('9', 'KILL', 'SIGKILL') and systeminfo.pid_isalive(pid):
terminate_task(pid, '-9', sleep, logger)
+ pids = settings.getValue('_EXECUTED_PIDS')
+ if pid in pids:
+ pids.remove(pid)
+ settings.setValue('_EXECUTED_PIDS', pids)
+
+def terminate_all_tasks(logger):
+ """Terminate all processes executed by vsperf, just for case they were not
+ terminated by standard means.
+ """
+ pids = settings.getValue('_EXECUTED_PIDS')
+ if pids:
+ logger.debug('Following processes will be terminated: %s', pids)
+ for pid in pids:
+ terminate_task_subtree(pid, logger=logger)
+ settings.setValue('_EXECUTED_PIDS', [])
+
class Process(object):
"""Control an instance of a long-running process.
diff --git a/tools/teststepstools.py b/tools/teststepstools.py
index 639e3437..33db8f79 100644
--- a/tools/teststepstools.py
+++ b/tools/teststepstools.py
@@ -19,6 +19,7 @@ import logging
import subprocess
import locale
from tools.functions import filter_output
+from tools.tasks import run_background_task
_LOGGER = logging.getLogger(__name__)
@@ -102,3 +103,19 @@ class TestStepsTools(object):
""" validate result of shell `command' execution
"""
return result is not None
+
+ @staticmethod
+ def Exec_Shell_Background(command):
+ """ Execute a shell `command' at the background and return its PID id
+ """
+ try:
+ pid = run_background_task(command.split(), _LOGGER, "Background task: {}".format(command))
+ return pid
+ except OSError:
+ return None
+
+ @staticmethod
+ def validate_Exec_Shell_Background(result, dummy_command, dummy_regex=None):
+ """ validate result of shell `command' execution on the background
+ """
+ return result is not None