paths: Modify algorithm for PATHS verification
[vswitchperf.git] / tools / pkt_gen / trex / trex.py
index ae26230..70864a5 100644 (file)
@@ -21,14 +21,50 @@ import subprocess
 import sys
 from collections import OrderedDict
 # pylint: disable=unused-import
+import netaddr
 import zmq
 from conf import settings
 from conf import merge_spec
 from core.results.results_constants import ResultsConstants
 from tools.pkt_gen.trafficgen.trafficgen import ITrafficGenerator
-# pylint: disable=wrong-import-position, import-error
-sys.path.append(settings.getValue('PATHS')['trafficgen']['trex']['src']['path'])
-from trex_stl_lib.api import *
+try:
+    # pylint: disable=wrong-import-position, import-error
+    sys.path.append(settings.getValue('PATHS')['trafficgen']['Trex']['src']['path'])
+    from trex_stl_lib.api import *
+except ImportError:
+    # VSPERF performs detection of T-Rex api during testcase initialization. So if
+    # T-Rex is requsted and API is not available it will fail before this code
+    # is reached.
+    # This code can be reached in case that --list-trafficgens is called, but T-Rex
+    # api is not installed. In this case we can ignore an exception, becuase T-Rex
+    # import won't be used.
+    pass
+
+_EMPTY_STATS = {
+    'global': {'bw_per_core': 0.0,
+               'cpu_util': 0.0,
+               'queue_full': 0.0,
+               'rx_bps': 0.0,
+               'rx_cpu_util': 0.0,
+               'rx_drop_bps': 0.0,
+               'rx_pps': 0.0,
+               'tx_bps': 0.0,
+               'tx_pps': 0.0,},
+    'latency': {},
+    'total': {'ibytes': 0.0,
+              'ierrors': 0.0,
+              'ipackets': 0.0,
+              'obytes': 0.0,
+              'oerrors': 0.0,
+              'opackets': 0.0,
+              'rx_bps': 0.0,
+              'rx_bps_L1': 0.0,
+              'rx_pps': 0.0,
+              'rx_util': 0.0,
+              'tx_bps': 0.0,
+              'tx_bps_L1': 0.0,
+              'tx_pps': 0.0,
+              'tx_util': 0.0,}}
 
 class Trex(ITrafficGenerator):
     """Trex Traffic generator wrapper."""
@@ -148,8 +184,46 @@ class Trex(ITrafficGenerator):
         fsize_no_fcs = frame_size - 4
         payload_a = max(0, fsize_no_fcs - len(base_pkt_a)) * 'x'
         payload_b = max(0, fsize_no_fcs - len(base_pkt_b)) * 'x'
-        pkt_a = STLPktBuilder(pkt=base_pkt_a/payload_a)
-        pkt_b = STLPktBuilder(pkt=base_pkt_b/payload_b)
+
+        # Multistream configuration, increments source values only
+        ms_mod = list() # mod list for incrementing values to be populated based on layer
+        if traffic['multistream'] > 1:
+            if traffic['stream_type'].upper() == 'L2':
+                for _ in [base_pkt_a, base_pkt_b]:
+                    ms_mod += [STLVmFlowVar(name="mac_start", min_value=0,
+                                            max_value=traffic['multistream'] - 1, size=4, op="inc"),
+                               STLVmWrFlowVar(fv_name="mac_start", pkt_offset=7)]
+            elif traffic['stream_type'].upper() == 'L3':
+                ip_src = {"start": int(netaddr.IPAddress(traffic['l3']['srcip'])),
+                          "end": int(netaddr.IPAddress(traffic['l3']['srcip'])) + traffic['multistream'] - 1}
+                ip_dst = {"start": int(netaddr.IPAddress(traffic['l3']['dstip'])),
+                          "end": int(netaddr.IPAddress(traffic['l3']['dstip'])) + traffic['multistream'] - 1}
+                for ip_address in [ip_src, ip_dst]:
+                    ms_mod += [STLVmFlowVar(name="ip_src", min_value=ip_address['start'],
+                                            max_value=ip_address['end'], size=4, op="inc"),
+                               STLVmWrFlowVar(fv_name="ip_src", pkt_offset="IP.src")]
+            elif traffic['stream_type'].upper() == 'L4':
+                for udpport in [traffic['l4']['srcport'], traffic['l4']['dstport']]:
+                    if udpport + (traffic['multistream'] - 1) > 65535:
+                        start_port = udpport
+                        # find the max/min port number based on the loop around of 65535 to 0 if needed
+                        minimum_value = 65535 - (traffic['multistream'] -1)
+                        maximum_value = 65535
+                    else:
+                        start_port, minimum_value = udpport, udpport
+                        maximum_value = start_port + (traffic['multistream'] - 1)
+                    ms_mod += [STLVmFlowVar(name="port_src", init_value=start_port,
+                                            min_value=minimum_value, max_value=maximum_value,
+                                            size=2, op="inc"),
+                               STLVmWrFlowVar(fv_name="port_src", pkt_offset="UDP.sport"),]
+
+        if ms_mod: # multistream detected
+            pkt_a = STLPktBuilder(pkt=base_pkt_a/payload_a, vm=[ms_mod[0], ms_mod[1]])
+            pkt_b = STLPktBuilder(pkt=base_pkt_b/payload_b, vm=[ms_mod[2], ms_mod[3]])
+        else:
+            pkt_a = STLPktBuilder(pkt=base_pkt_a / payload_a)
+            pkt_b = STLPktBuilder(pkt=base_pkt_b / payload_b)
+
         stream_1 = STLStream(packet=pkt_a,
                              name='stream_1',
                              mode=STLTXCont(percentage=traffic['frame_rate']))
@@ -175,6 +249,10 @@ class Trex(ITrafficGenerator):
         my_ports = [0, 1]
         self._stlclient.reset(my_ports)
         ports_info = self._stlclient.get_port_info(my_ports)
+        # for SR-IOV
+        if settings.getValue('TRAFFICGEN_TREX_PROMISCUOUS'):
+            self._stlclient.set_port_attr(my_ports, promiscuous=True)
+
         packet_1, packet_2 = Trex.create_packets(traffic, ports_info)
         stream_1, stream_2, stream_1_lat, stream_2_lat = Trex.create_streams(packet_1, packet_2, traffic)
         self._stlclient.add_streams(stream_1, ports=[0])
@@ -213,13 +291,15 @@ class Trex(ITrafficGenerator):
         result[ResultsConstants.TX_RATE_PERCENT] = 'Unknown'
 
         result[ResultsConstants.THROUGHPUT_RX_PERCENT] = 'Unknown'
+        if stats["total"]["opackets"]:
+            result[ResultsConstants.FRAME_LOSS_PERCENT] = (
+                '{:.3f}'.format(
+                    float((stats["total"]["opackets"] - stats["total"]["ipackets"]) * 100 /
+                          stats["total"]["opackets"])))
+        else:
+            result[ResultsConstants.FRAME_LOSS_PERCENT] = 100
 
-        result[ResultsConstants.FRAME_LOSS_PERCENT] = (
-            '{:.3f}'.format(
-                float((stats["total"]["opackets"] - stats["total"]["ipackets"]) * 100 /
-                      stats["total"]["opackets"])))
-
-        if settings.getValue('TRAFFICGEN_TREX_LATENCY_PPS') > 0:
+        if settings.getValue('TRAFFICGEN_TREX_LATENCY_PPS') > 0 and stats['latency']:
             result[ResultsConstants.MIN_LATENCY_NS] = (
                 '{:.3f}'.format(
                     (float(min(stats["latency"][0]["latency"]["total_min"],
@@ -266,15 +346,17 @@ class Trex(ITrafficGenerator):
         raise NotImplementedError(
             'Trex stop_cont_traffic method not implemented')
 
-    def send_rfc2544_throughput(self, traffic=None, duration=60,
-                                lossrate=0.0, tests=10):
+    def send_rfc2544_throughput(self, traffic=None, tests=1, duration=60,
+                                lossrate=0.0):
         """See ITrafficGenerator for description
         """
         self._logger.info("In Trex send_rfc2544_throughput method")
         self._params.clear()
+        threshold = settings.getValue('TRAFFICGEN_TREX_RFC2544_TPUT_THRESHOLD')
         test_lossrate = 0
         left = 0
-        num_test = 1
+        iteration = 1
+        stats_ok = _EMPTY_STATS
         self._params['traffic'] = self.traffic_defaults.copy()
         if traffic:
             self._params['traffic'] = merge_spec(
@@ -284,13 +366,16 @@ class Trex(ITrafficGenerator):
         right = traffic['frame_rate']
         center = traffic['frame_rate']
 
-        while num_test <= tests:
+        # Loops until the preconfigured difference between frame rate
+        # of successful and unsuccessful iterations is reached
+        while (right - left) > threshold:
             test_lossrate = ((stats["total"]["opackets"] - stats["total"]
                               ["ipackets"]) * 100) / stats["total"]["opackets"]
             self._logger.debug("Iteration: %s, frame rate: %s, throughput_rx_fps: %s, frame_loss_percent: %s",
-                               num_test, "{:.3f}".format(new_params['frame_rate']), stats['total']['rx_pps'],
+                               iteration, "{:.3f}".format(new_params['frame_rate']), stats['total']['rx_pps'],
                                "{:.3f}".format(test_lossrate))
             if test_lossrate == 0.0 and new_params['frame_rate'] == traffic['frame_rate']:
+                stats_ok = copy.deepcopy(stats)
                 break
             elif test_lossrate > lossrate:
                 right = center
@@ -299,13 +384,14 @@ class Trex(ITrafficGenerator):
                 new_params['frame_rate'] = center
                 stats = self.generate_traffic(new_params, duration)
             else:
+                stats_ok = copy.deepcopy(stats)
                 left = center
                 center = (left+right) / 2
                 new_params = copy.deepcopy(traffic)
                 new_params['frame_rate'] = center
                 stats = self.generate_traffic(new_params, duration)
-            num_test += 1
-        return self.calculate_results(stats)
+            iteration += 1
+        return self.calculate_results(stats_ok)
 
     def start_rfc2544_throughput(self, traffic=None, tests=1, duration=60,
                                  lossrate=0.0):