diff options
author | Martin Klozik <martinx.klozik@intel.com> | 2017-12-19 08:58:43 +0000 |
---|---|---|
committer | Martin Klozik <martinx.klozik@intel.com> | 2018-01-11 11:28:02 +0000 |
commit | a2d97b05fb3b7ff62f62bcb16d36c51e3df6ca77 (patch) | |
tree | 8cb4e2cd7a463275416d8ac2422b6907166f2a94 | |
parent | 58d53561bad01ae7829fdeee0c67f5dae4a9dc34 (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__.py | 13 | ||||
-rw-r--r-- | conf/integration/01_testcases.conf | 121 | ||||
-rw-r--r-- | docs/testing/user/userguide/teststeps.rst | 4 | ||||
-rw-r--r-- | testcases/testcase.py | 3 | ||||
-rw-r--r-- | tools/tasks.py | 33 | ||||
-rw-r--r-- | tools/teststepstools.py | 17 |
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 |