aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Klozik <martinx.klozik@intel.com>2018-01-04 10:32:48 +0000
committerMartin Klozik <martinx.klozik@intel.com>2018-01-12 09:43:57 +0000
commitce8e18ddcfb84f887aa91f38133781da2f592309 (patch)
tree117e7ae47b15326efc18ec5ca87a5315ac930a9a
parenta2d97b05fb3b7ff62f62bcb16d36c51e3df6ca77 (diff)
trex: Add support for traffic capture
A support of traffic capture was added into T-Rex. It allows to write a functional tests, which will verify proper vSwitch functionality by inspection of packets received by T-Rex. A testcase example was added into integration testcases. JIRA: VSPERF-556 Change-Id: I5ad28479ca2ec29760b68f24510af1a6d74866ae Signed-off-by: Martin Klozik <martinx.klozik@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> Reviewed-by: Richard Elias <richardx.elias@intel.com>
-rw-r--r--conf/03_traffic.conf31
-rw-r--r--conf/integration/01_testcases.conf38
-rw-r--r--core/results/results_constants.py4
-rw-r--r--docs/testing/developer/devguide/design/vswitchperf_design.rst24
-rw-r--r--docs/testing/user/configguide/trafficgen.rst10
-rw-r--r--tools/pkt_gen/trex/trex.py59
6 files changed, 155 insertions, 11 deletions
diff --git a/conf/03_traffic.conf b/conf/03_traffic.conf
index 3c7bd2f5..67318893 100644
--- a/conf/03_traffic.conf
+++ b/conf/03_traffic.conf
@@ -147,6 +147,30 @@ LOG_FILE_TRAFFIC_GEN = 'traffic-gen.log'
# congestion (DEI header field).
# Data type: int (NOTE: must fit to 1 bit)
# Default value: 0
+# 'capture' - A dictionary with traffic capture configuration.
+# NOTE: It is supported only by T-Rex traffic generator.
+# 'enabled' - Specifies if traffic should be captured
+# Data type: bool
+# Default value: False
+# 'tx_ports' - A list of ports, where frames transmitted towards DUT will
+# be captured. Ports have numbers 0 and 1. TX packet capture
+# is disabled if list of ports is empty.
+# Data type: list
+# Default value: [0]
+# 'rx_ports' - A list of ports, where frames received from DUT will
+# be captured. Ports have numbers 0 and 1. RX packet capture
+# is disabled if list of ports is empty.
+# Data type: list
+# Default value: [1]
+# 'count' - A number of frames to be captured. The same count value
+# is applied to both TX and RX captures.
+# Data type: int
+# Default value: 1
+# 'filter' - An expression used to filter TX and RX packets. It uses the same
+# syntax as pcap library. See pcap-filter man page for additional
+# details.
+# Data type: str
+# Default value: ''
TRAFFIC = {
'traffic_type' : 'rfc2544_throughput',
'frame_rate' : 100,
@@ -179,6 +203,13 @@ TRAFFIC = {
'priority': 0,
'cfi': 0,
},
+ 'capture': {
+ 'enabled': False,
+ 'tx_ports' : [0],
+ 'rx_ports' : [1],
+ 'count': 1,
+ 'filter': '',
+ },
}
#path to traffic generators directory.
diff --git a/conf/integration/01_testcases.conf b/conf/integration/01_testcases.conf
index 692f1561..bb2809b8 100644
--- a/conf/integration/01_testcases.conf
+++ b/conf/integration/01_testcases.conf
@@ -1118,6 +1118,44 @@ INTEGRATION_TESTS += [
['tools', 'assert', '#STEP[-1][0] == 0'],
],
},
+ # Capture Example 3 - Traffic capture by traffic generator.
+ # This TestCase uses OVS flow to add VLAN tag with given ID into every
+ # frame send by traffic generator. Correct frame modificaiton is verified by
+ # inspection of packet capture received by T-Rex.
+ {
+ "Name": "capture_p2p_add_vlan_ovs_trex",
+ "Deployment": "clean",
+ "Description": "OVS: Test VLAN tag modification and verify it by traffic capture",
+ "vSwitch" : "OvsDpdkVhost", # works also for Vanilla OVS
+ "Parameters" : {
+ "TRAFFICGEN" : "Trex",
+ "TRAFFICGEN_DURATION" : 5,
+ "TRAFFIC" : {
+ "traffic_type" : "rfc2544_continuous",
+ "frame_rate" : 100,
+ # enable capture of five RX frames
+ 'capture': {
+ 'enabled': True,
+ 'tx_ports' : [],
+ 'rx_ports' : [1],
+ 'count' : 5,
+ },
+ },
+ },
+ "TestSteps" : STEP_VSWITCH_P2P_INIT + [
+ # replace standard L2 flows by flows, which will add VLAN tag with ID 3
+ ['!vswitch', 'add_flow', 'int_br0', {'in_port': '1', 'actions': ['mod_vlan_vid:3','output:2']}],
+ ['!vswitch', 'add_flow', 'int_br0', {'in_port': '2', 'actions': ['mod_vlan_vid:3','output:1']}],
+ ['vswitch', 'dump_flows', 'int_br0'],
+ ['trafficgen', 'send_traffic', {}],
+ ['trafficgen', 'get_results'],
+ # verify that captured frames have vlan tag with ID 3
+ ['tools', 'exec_shell', 'tcpdump -qer $RESULTS_PATH/#STEP[-1][0]["capture_rx"] vlan 3 '
+ '2>/dev/null | wc -l', '|^(\d+)$'],
+ # number of received frames with expected VLAN id must match the number of captured frames
+ ['tools', 'assert', '#STEP[-1][0] == 5'],
+ ] + STEP_VSWITCH_P2P_FINIT,
+ },
#
# End of examples of functional testcases with traffic capture validation
#
diff --git a/core/results/results_constants.py b/core/results/results_constants.py
index ef2df847..967adbf9 100644
--- a/core/results/results_constants.py
+++ b/core/results/results_constants.py
@@ -69,6 +69,10 @@ class ResultsConstants(object):
TEST_START_TIME = "start_time"
TEST_STOP_TIME = "stop_time"
+ # files with traffic capture
+ CAPTURE_TX = "capture_tx"
+ CAPTURE_RX = "capture_rx"
+
@staticmethod
def get_traffic_constants():
"""Method returns all Constants used to store results.
diff --git a/docs/testing/developer/devguide/design/vswitchperf_design.rst b/docs/testing/developer/devguide/design/vswitchperf_design.rst
index 33051493..96ffcf62 100644
--- a/docs/testing/developer/devguide/design/vswitchperf_design.rst
+++ b/docs/testing/developer/devguide/design/vswitchperf_design.rst
@@ -415,6 +415,30 @@ Detailed description of ``TRAFFIC`` dictionary items follows:
congestion (DEI header field).
Data type: int (NOTE: must fit to 1 bit)
Default value: 0
+ 'capture' - A dictionary with traffic capture configuration.
+ NOTE: It is supported only by T-Rex traffic generator.
+ 'enabled' - Specifies if traffic should be captured
+ Data type: bool
+ Default value: False
+ 'tx_ports' - A list of ports, where frames transmitted towards DUT will
+ be captured. Ports have numbers 0 and 1. TX packet capture
+ is disabled if list of ports is empty.
+ Data type: list
+ Default value: [0]
+ 'rx_ports' - A list of ports, where frames received from DUT will
+ be captured. Ports have numbers 0 and 1. RX packet capture
+ is disabled if list of ports is empty.
+ Data type: list
+ Default value: [1]
+ 'count' - A number of frames to be captured. The same count value
+ is applied to both TX and RX captures.
+ Data type: int
+ Default value: 1
+ 'filter' - An expression used to filter TX and RX packets. It uses the same
+ syntax as pcap library. See pcap-filter man page for additional
+ details.
+ Data type: str
+ Default value: ''
.. _configuration-of-guest-options:
diff --git a/docs/testing/user/configguide/trafficgen.rst b/docs/testing/user/configguide/trafficgen.rst
index 91c4084e..029247f2 100644
--- a/docs/testing/user/configguide/trafficgen.rst
+++ b/docs/testing/user/configguide/trafficgen.rst
@@ -44,7 +44,8 @@ and is configured as follows:
'stream_type' : 'L4',
'pre_installed_flows' : 'No', # used by vswitch implementation
'flow_type' : 'port', # used by vswitch implementation
-
+ 'flow_control' : False, # supported only by IxNet
+ 'learning_frames' : True, # supported only by IxNet
'l2': {
'framesize': 64,
'srcmac': '00:00:00:00:00:00',
@@ -67,6 +68,13 @@ and is configured as follows:
'priority': 0,
'cfi': 0,
},
+ 'capture': {
+ 'enabled': False,
+ 'tx_ports' : [0],
+ 'rx_ports' : [1],
+ 'count': 1,
+ 'filter': '',
+ },
}
The framesize parameter can be overridden from the configuration
diff --git a/tools/pkt_gen/trex/trex.py b/tools/pkt_gen/trex/trex.py
index 82118f6f..5ce87b1d 100644
--- a/tools/pkt_gen/trex/trex.py
+++ b/tools/pkt_gen/trex/trex.py
@@ -20,6 +20,7 @@ import logging
import subprocess
import sys
import time
+import os
from collections import OrderedDict
# pylint: disable=unused-import
import netaddr
@@ -91,11 +92,11 @@ class Trex(ITrafficGenerator):
the configuration file
'''
self._stlclient = STLClient()
- self._logger.info("TREX: In Trex connect method...")
+ self._logger.info("T-Rex: In Trex connect method...")
if self._trex_host_ip_addr:
cmd_ping = "ping -c1 " + self._trex_host_ip_addr
else:
- raise RuntimeError('TREX: Trex host not defined')
+ raise RuntimeError('T-Rex: Trex host not defined')
ping = subprocess.Popen(cmd_ping, shell=True, stderr=subprocess.PIPE)
output, error = ping.communicate()
@@ -103,7 +104,7 @@ class Trex(ITrafficGenerator):
if ping.returncode:
self._logger.error(error)
self._logger.error(output)
- raise RuntimeError('TREX: Cannot ping Trex host at ' + \
+ raise RuntimeError('T-Rex: Cannot ping Trex host at ' + \
self._trex_host_ip_addr)
connect_trex = "ssh " + self._trex_user + \
@@ -122,13 +123,13 @@ class Trex(ITrafficGenerator):
self._logger.error(error)
self._logger.error(output)
raise RuntimeError(
- 'TREX: Cannot locate Trex program at %s within %s' \
+ 'T-Rex: Cannot locate Trex program at %s within %s' \
% (self._trex_host_ip_addr, self._trex_base_dir))
self._stlclient = STLClient(username=self._trex_user, server=self._trex_host_ip_addr,
verbose_level=0)
self._stlclient.connect()
- self._logger.info("TREX: Trex host successfully found...")
+ self._logger.info("T-Rex: Trex host successfully found...")
def disconnect(self):
"""Disconnect from the traffic generator.
@@ -140,7 +141,7 @@ class Trex(ITrafficGenerator):
:returns: None
"""
- self._logger.info("TREX: In trex disconnect method")
+ self._logger.info("T-Rex: In trex disconnect method")
self._stlclient.disconnect(stop_traffic=True, release_ports=True)
@staticmethod
@@ -245,11 +246,16 @@ class Trex(ITrafficGenerator):
return (stream_1, stream_2, stream_1_lat, stream_2_lat)
- def generate_traffic(self, traffic, duration):
+ def generate_traffic(self, traffic, duration, disable_capture=False):
"""The method that generate a stream
"""
my_ports = [0, 1]
+
+ # initialize ports
self._stlclient.reset(my_ports)
+ self._stlclient.remove_all_captures()
+ self._stlclient.set_service_mode(ports=my_ports, enabled=False)
+
ports_info = self._stlclient.get_port_info(my_ports)
# for SR-IOV
if settings.getValue('TRAFFICGEN_TREX_PROMISCUOUS'):
@@ -264,10 +270,35 @@ class Trex(ITrafficGenerator):
self._stlclient.add_streams(stream_1_lat, ports=[0])
self._stlclient.add_streams(stream_2_lat, ports=[1])
+ # enable traffic capture if requested
+ pcap_id = {}
+ if traffic['capture']['enabled'] and not disable_capture:
+ for ports in ['tx_ports', 'rx_ports']:
+ if traffic['capture'][ports]:
+ pcap_dir = ports[:2]
+ self._logger.info("T-Rex starting %s traffic capture", pcap_dir.upper())
+ capture = {ports : traffic['capture'][ports],
+ 'limit' : traffic['capture']['count'],
+ 'bpf_filter' : traffic['capture']['filter']}
+ self._stlclient.set_service_mode(ports=traffic['capture'][ports], enabled=True)
+ pcap_id[pcap_dir] = self._stlclient.start_capture(**capture)
+
self._stlclient.clear_stats()
- self._stlclient.start(ports=[0, 1], force=True, duration=duration)
- self._stlclient.wait_on_traffic(ports=[0, 1])
+ self._stlclient.start(ports=my_ports, force=True, duration=duration)
+ self._stlclient.wait_on_traffic(ports=my_ports)
stats = self._stlclient.get_stats(sync_now=True)
+
+ # export captured data into pcap file if possible
+ if pcap_id:
+ for pcap_dir in pcap_id:
+ pcap_file = 'capture_{}.pcap'.format(pcap_dir)
+ self._stlclient.stop_capture(pcap_id[pcap_dir]['id'],
+ os.path.join(settings.getValue('RESULTS_PATH'), pcap_file))
+ stats['capture_{}'.format(pcap_dir)] = pcap_file
+ self._logger.info("T-Rex writing %s traffic capture into %s", pcap_dir.upper(), pcap_file)
+ # disable service mode for all ports used by Trex
+ self._stlclient.set_service_mode(ports=my_ports, enabled=False)
+
return stats
@staticmethod
@@ -325,6 +356,11 @@ class Trex(ITrafficGenerator):
result[ResultsConstants.MIN_LATENCY_NS] = 'Unknown'
result[ResultsConstants.MAX_LATENCY_NS] = 'Unknown'
result[ResultsConstants.AVG_LATENCY_NS] = 'Unknown'
+
+ if 'capture_tx' in stats:
+ result[ResultsConstants.CAPTURE_TX] = stats['capture_tx']
+ if 'capture_rx' in stats:
+ result[ResultsConstants.CAPTURE_RX] = stats['capture_rx']
return result
def learning_packets(self, traffic):
@@ -336,7 +372,9 @@ class Trex(ITrafficGenerator):
self._logger.info("T-Rex sending learning packets")
learning_thresh_traffic = copy.deepcopy(traffic)
learning_thresh_traffic["frame_rate"] = 1
- self.generate_traffic(learning_thresh_traffic, settings.getValue("TRAFFICGEN_TREX_LEARNING_DURATION"))
+ self.generate_traffic(learning_thresh_traffic,
+ settings.getValue("TRAFFICGEN_TREX_LEARNING_DURATION"),
+ disable_capture=True)
self._logger.info("T-Rex finished learning packets")
time.sleep(3) # allow packets to complete before starting test traffic
@@ -403,6 +441,7 @@ class Trex(ITrafficGenerator):
if settings.getValue('TRAFFICGEN_TREX_LEARNING_MODE'):
self.learning_packets(traffic)
+ self._logger.info("T-Rex sending traffic")
stats = self.generate_traffic(traffic, duration)
return self.calculate_results(stats)