Merge "Run testcase 074 result overridden by job status" into stable/gambia
[yardstick.git] / yardstick / network_services / traffic_profile / rfc2544.py
index 0e1dbd5..e33c437 100644 (file)
@@ -19,6 +19,7 @@ from trex_stl_lib import trex_stl_client
 from trex_stl_lib import trex_stl_packet_builder_scapy
 from trex_stl_lib import trex_stl_streams
 
+from yardstick.common import constants
 from yardstick.network_services.traffic_profile import trex_traffic_profile
 
 
@@ -70,7 +71,7 @@ class PortPgIDMap(object):
 class RFC2544Profile(trex_traffic_profile.TrexProfile):
     """TRex RFC2544 traffic profile"""
 
-    TOLERANCE_LIMIT = 0.05
+    TOLERANCE_LIMIT = 0.01
 
     def __init__(self, traffic_generator):
         super(RFC2544Profile, self).__init__(traffic_generator)
@@ -118,7 +119,8 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile):
                 ports.append(port_num)
                 port_pg_id.add_port(port_num)
                 profile = self._create_profile(profile_data,
-                                               self.rate, port_pg_id)
+                                               self.rate, port_pg_id,
+                                               self.config.enable_latency)
                 self.generator.client.add_streams(profile, ports=[port_num])
 
         self.generator.client.start(ports=ports,
@@ -126,7 +128,7 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile):
                                     force=True)
         return ports, port_pg_id
 
-    def _create_profile(self, profile_data, rate, port_pg_id):
+    def _create_profile(self, profile_data, rate, port_pg_id, enable_latency):
         """Create a STL profile (list of streams) for a port"""
         streams = []
         for packet_name in profile_data:
@@ -134,11 +136,13 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile):
                     get('outer_l2', {}).get('framesize'))
             imix_data = self._create_imix_data(imix)
             self._create_vm(profile_data[packet_name])
-            _streams = self._create_streams(imix_data, rate, port_pg_id)
+            _streams = self._create_streams(imix_data, rate, port_pg_id,
+                                            enable_latency)
             streams.extend(_streams)
         return trex_stl_streams.STLProfile(streams)
 
-    def _create_imix_data(self, imix):
+    def _create_imix_data(self, imix,
+                          weight_mode=constants.DISTRIBUTION_IN_PACKETS):
         """Generate the IMIX distribution for a STL profile
 
         The input information is the framesize dictionary in a test case
@@ -157,6 +161,20 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile):
         E.g.:
           imix_count = {64: 25, 128: 75}
 
+        The weight mode is described in [1]. There are two ways to describe the
+        weight of the packets:
+          - Distribution in packets: the weight defines the percentage of
+            packets sent per packet size. IXIA uses this definition.
+          - Distribution in bytes: the weight defines the percentage of bytes
+            sent per packet size.
+
+        Packet size  # packets  D. in packets  Bytes  D. in bytes
+        40           7          58.33%         280    7%
+        576          4          33.33%         2304   56%
+        1500         1          8.33%          1500   37%
+
+        [1] https://en.wikipedia.org/wiki/Internet_Mix
+
         :param imix: (dict) IMIX size and weight
         """
         imix_count = {}
@@ -171,8 +189,16 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile):
             imix_sum = 100
 
         weight_normalize = float(imix_sum) / 100
-        return {size: float(weight) / weight_normalize
-                for size, weight in imix_count.items()}
+        imix_dip = {size: float(weight) / weight_normalize
+                    for size, weight in imix_count.items()}
+
+        if weight_mode == constants.DISTRIBUTION_IN_BYTES:
+            return imix_dip
+
+        byte_total = sum([int(size) * weight
+                          for size, weight in imix_dip.items()])
+        return {size: (int(size) * weight * 100) / byte_total
+                for size, weight in imix_dip.items()}
 
     def _create_vm(self, packet_definition):
         """Create the STL Raw instructions"""
@@ -213,7 +239,7 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile):
         return trex_stl_packet_builder_scapy.STLPktBuilder(
             pkt=base_pkt / pad, vm=self.trex_vm)
 
-    def _create_streams(self, imix_data, rate, port_pg_id):
+    def _create_streams(self, imix_data, rate, port_pg_id, enable_latency):
         """Create a list of streams per packet size
 
         The STL TX mode speed of the generated streams will depend on the frame
@@ -237,7 +263,8 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile):
                              in imix_data.items() if float(weight) > 0):
             packet = self._create_single_packet(size)
             pg_id = port_pg_id.increase_pg_id()
-            stl_flow = trex_stl_streams.STLFlowLatencyStats(pg_id=pg_id)
+            stl_flow = (trex_stl_streams.STLFlowLatencyStats(pg_id=pg_id) if
+                        enable_latency else None)
             mode = trex_stl_streams.STLTXCont(percentage=weight * rate / 100)
             streams.append(trex_stl_client.STLStream(
                 packet=packet, flow_stats=stl_flow, mode=mode))
@@ -246,19 +273,17 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile):
     def get_drop_percentage(self, samples, tol_low, tol_high,
                             correlated_traffic):
         """Calculate the drop percentage and run the traffic"""
-        tx_rate_fps = 0
-        rx_rate_fps = 0
-        for sample in samples:
-            tx_rate_fps += sum(
-                port['tx_throughput_fps'] for port in sample.values())
-            rx_rate_fps += sum(
-                port['rx_throughput_fps'] for port in sample.values())
-        tx_rate_fps = round(float(tx_rate_fps) / len(samples), 2)
-        rx_rate_fps = round(float(rx_rate_fps) / len(samples), 2)
-
-        # TODO(esm): RFC2544 doesn't tolerate packet loss, why do we?
-        out_packets = sum(port['out_packets'] for port in samples[-1].values())
-        in_packets = sum(port['in_packets'] for port in samples[-1].values())
+        completed = False
+        out_pkt_end = sum(port['out_packets'] for port in samples[-1].values())
+        in_pkt_end = sum(port['in_packets'] for port in samples[-1].values())
+        out_pkt_ini = sum(port['out_packets'] for port in samples[0].values())
+        in_pkt_ini = sum(port['in_packets'] for port in samples[0].values())
+        time_diff = (list(samples[-1].values())[0]['timestamp'] -
+                     list(samples[0].values())[0]['timestamp']).total_seconds()
+        out_packets = out_pkt_end - out_pkt_ini
+        in_packets = in_pkt_end - in_pkt_ini
+        tx_rate_fps = float(out_packets) / time_diff
+        rx_rate_fps = float(in_packets) / time_diff
         drop_percent = 100.0
 
         # https://tools.ietf.org/html/rfc2544#section-26.3
@@ -266,15 +291,15 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile):
             drop_percent = round(
                 (float(abs(out_packets - in_packets)) / out_packets) * 100, 5)
 
-        tol_high = tol_high if tol_high > self.TOLERANCE_LIMIT else tol_high
-        tol_low = tol_low if tol_low > self.TOLERANCE_LIMIT else tol_low
+        tol_high = max(tol_high, self.TOLERANCE_LIMIT)
+        tol_low = min(tol_low, self.TOLERANCE_LIMIT)
         if drop_percent > tol_high:
             self.max_rate = self.rate
         elif drop_percent < tol_low:
             self.min_rate = self.rate
-        else:
-            # NOTE(ralonsoh): the test should finish here
-            # pass
+        else:
+            completed = True
+
         last_rate = self.rate
         self.rate = round(float(self.max_rate + self.min_rate) / 2.0, 5)
 
@@ -295,4 +320,4 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile):
             'Rate': last_rate,
             'Latency': latency
         }
-        return output
+        return completed, output