Support unidirection in all searches 30/21830/12
authorVratko Polak <vrpolak@cisco.com>
Fri, 6 Sep 2019 08:14:45 +0000 (10:14 +0200)
committerVratko Polak <vrpolak@cisco.com>
Thu, 12 Sep 2019 16:14:55 +0000 (18:14 +0200)
+ Rename bool unidirection to int traffic_directions.
+ Rename "untagged" to "initial" for bandwidth calculation.
+ Fix latency measurement for unidirectional traffic.
+ Remove duplicate colon in soak test message.
 + Edit PAL to accept both forms.
+ Fix minor documentation issues.

Change-Id: I6c76f2dc090ae493f2fbd7e9ccd45229d2306dea
Signed-off-by: Vratko Polak <vrpolak@cisco.com>
resources/libraries/python/TrafficGenerator.py
resources/libraries/robot/performance/performance_utils.robot
resources/tools/presentation/input_data_parser.py
resources/tools/trex/trex_stateless_profile.py

index 2e59459..f602029 100644 (file)
@@ -428,7 +428,7 @@ class TrafficGenerator(AbstractMeasurer):
 
     def trex_stl_start_remote_exec(
             self, duration, rate, frame_size, traffic_profile, async_call=False,
-            latency=True, warmup_time=5.0, unidirection=False, tx_port=0,
+            latency=True, warmup_time=5.0, traffic_directions=2, tx_port=0,
             rx_port=1):
         """Execute script on remote node over ssh to start traffic.
 
@@ -440,7 +440,8 @@ class TrafficGenerator(AbstractMeasurer):
         :param async_call: If enabled then don't wait for all incomming trafic.
         :param latency: With latency measurement.
         :param warmup_time: Warmup time period.
-        :param unidirection: Traffic is unidirectional. Default: False
+        :param traffic_directions: Traffic is bi- (2) or uni- (1) directional.
+            Default: 2
         :param tx_port: Traffic generator transmit port for first flow.
             Default: 0
         :param rx_port: Traffic generator receive port for first flow.
@@ -452,7 +453,7 @@ class TrafficGenerator(AbstractMeasurer):
         :type async_call: bool
         :type latency: bool
         :type warmup_time: float
-        :type unidirection: bool
+        :type traffic_directions: int
         :type tx_port: int
         :type rx_port: int
         :raises RuntimeError: In case of TG driver issue.
@@ -472,17 +473,16 @@ class TrafficGenerator(AbstractMeasurer):
             "sh -c '{tool}/resources/tools/trex/trex_stateless_profile.py"
             " --profile {prof}/resources/traffic_profiles/trex/{traffic}.py"
             " --duration {duration!r} --frame_size {frame_size} --rate {rate!r}"
-            " --warmup_time {warmup!r} --port_0 {p_0} --port_1 {p_1}").format(
+            " --warmup_time {warmup!r} --port_0 {p_0} --port_1 {p_1}"
+            " --traffic_directions {dirs}").format(
                 tool=Constants.REMOTE_FW_DIR, prof=Constants.REMOTE_FW_DIR,
                 traffic=traffic_profile, duration=duration,
                 frame_size=frame_size, rate=rate, warmup=warmup_time, p_0=p_0,
-                p_1=p_1)
+                p_1=p_1, dirs=traffic_directions)
         if async_call:
             command += " --async"
         if latency:
             command += " --latency"
-        if unidirection:
-            command += " --unidirection"
         command += "'"
 
         stdout, _ = exec_cmd_no_error(
@@ -514,7 +514,7 @@ class TrafficGenerator(AbstractMeasurer):
 
     def send_traffic_on_tg(
             self, duration, rate, frame_size, traffic_profile, warmup_time=5,
-            async_call=False, latency=True, unidirection=False, tx_port=0,
+            async_call=False, latency=True, traffic_directions=2, tx_port=0,
             rx_port=1):
         """Send traffic from all configured interfaces on TG.
 
@@ -540,7 +540,8 @@ class TrafficGenerator(AbstractMeasurer):
         :param warmup_time: Warmup phase in seconds.
         :param async_call: Async mode.
         :param latency: With latency measurement.
-        :param unidirection: Traffic is unidirectional. Default: False
+        :param traffic_directions: Traffic is bi- (2) or uni- (1) directional.
+            Default: 2
         :param tx_port: Traffic generator transmit port for first flow.
             Default: 0
         :param rx_port: Traffic generator receive port for first flow.
@@ -552,7 +553,7 @@ class TrafficGenerator(AbstractMeasurer):
         :type warmup_time: float
         :type async_call: bool
         :type latency: bool
-        :type unidirection: bool
+        :type traffic_directions: int
         :type tx_port: int
         :type rx_port: int
         :returns: TG output.
@@ -565,7 +566,7 @@ class TrafficGenerator(AbstractMeasurer):
         if subtype == NodeSubTypeTG.TREX:
             self.trex_stl_start_remote_exec(
                 duration, rate, frame_size, traffic_profile, async_call,
-                latency, warmup_time, unidirection, tx_port, rx_port)
+                latency, warmup_time, traffic_directions, tx_port, rx_port)
 
         return self._result
 
@@ -617,20 +618,24 @@ class TrafficGenerator(AbstractMeasurer):
                 loss, loss_acceptance))
 
     def set_rate_provider_defaults(self, frame_size, traffic_profile,
-                                   warmup_time=0.0):
+                                   warmup_time=0.0, traffic_directions=2):
         """Store values accessed by measure().
 
         :param frame_size: Frame size identifier or value [B].
         :param traffic_profile: Module name as a traffic profile identifier.
             See resources/traffic_profiles/trex for implemented modules.
         :param warmup_time: Traffic duration before measurement starts [s].
+        :param traffic_directions: Traffic is bi- (2) or uni- (1) directional.
+            Default: 2
         :type frame_size: str or int
         :type traffic_profile: str
         :type warmup_time: float
+        :type traffic_directions: int
         """
         self.frame_size = frame_size
         self.traffic_profile = str(traffic_profile)
         self.warmup_time = float(warmup_time)
+        self.traffic_directions = traffic_directions
 
     def get_measurement_result(self, duration=None, transmit_rate=None):
         """Return the result of last measurement as ReceiveRateMeasurement.
@@ -653,8 +658,7 @@ class TrafficGenerator(AbstractMeasurer):
             duration = time.time() - self._start_time
             self._start_time = None
         if transmit_rate is None:
-            # Assuming bi-directional traffic here.
-            transmit_rate = self._rate * 2.0
+            transmit_rate = self._rate * (1.0 if self.uinidirection else 2.0)
         transmit_count = int(self.get_sent())
         loss_count = int(self.get_loss())
         measurement = ReceiveRateMeasurement(
@@ -663,10 +667,12 @@ class TrafficGenerator(AbstractMeasurer):
         return measurement
 
     def measure(self, duration, transmit_rate):
-        """Run bi-directional measurement, parse and return results.
+        """Run trial measurement, parse and return aggregate results.
+
+        Aggregate means sum over traffic directions.
 
         :param duration: Trial duration [s].
-        :param transmit_rate: Target bidirectional transmit rate [pps].
+        :param transmit_rate: Target aggregate transmit rate [pps].
         :type duration: float
         :type transmit_rate: float
         :returns: Structure containing the result of the measurement.
@@ -678,15 +684,21 @@ class TrafficGenerator(AbstractMeasurer):
         duration = float(duration)
         transmit_rate = float(transmit_rate)
         # TG needs target Tr per stream, but reports aggregate Tx and Dx.
-        unit_rate = str(transmit_rate / 2.0) + "pps"
+        unit_rate_int = transmit_rate / float(self.traffic_directions)
+        unit_rate_str = str(unit_rate_int) + "pps"
         self.send_traffic_on_tg(
-            duration, unit_rate, self.frame_size, self.traffic_profile,
-            warmup_time=self.warmup_time, latency=True)
+            duration, unit_rate_str, self.frame_size, self.traffic_profile,
+            warmup_time=self.warmup_time, latency=True,
+            traffic_directions=self.traffic_directions)
         return self.get_measurement_result(duration, transmit_rate)
 
 
 class OptimizedSearch(object):
-    """Class to be imported as Robot Library, containing a single keyword."""
+    """Class to be imported as Robot Library, containing search keywords.
+
+    Aside of setting up measurer and forwarding arguments,
+    the main business is to translate min/max rate from unidir to aggregate.
+    """
 
     @staticmethod
     def perform_optimized_ndrpdr_search(
@@ -694,15 +706,15 @@ class OptimizedSearch(object):
             maximum_transmit_rate, packet_loss_ratio=0.005,
             final_relative_width=0.005, final_trial_duration=30.0,
             initial_trial_duration=1.0, number_of_intermediate_phases=2,
-            timeout=720.0, doublings=1):
+            timeout=720.0, doublings=1, traffic_directions=2):
         """Setup initialized TG, perform optimized search, return intervals.
 
         :param frame_size: Frame size identifier or value [B].
         :param traffic_profile: Module name as a traffic profile identifier.
             See resources/traffic_profiles/trex for implemented modules.
-        :param minimum_transmit_rate: Minimal bidirectional
+        :param minimum_transmit_rate: Minimal uni-directional
             target transmit rate [pps].
-        :param maximum_transmit_rate: Maximal bidirectional
+        :param maximum_transmit_rate: Maximal uni-directional
             target transmit rate [pps].
         :param packet_loss_ratio: Fraction of packets lost, for PDR [1].
         :param final_relative_width: Final lower bound transmit rate
@@ -717,6 +729,8 @@ class OptimizedSearch(object):
         :param doublings: How many doublings to do in external search step.
             Default 1 is suitable for fairly stable tests,
             less stable tests might get better overal duration with 2 or more.
+        :param traffic_directions: Traffic is bi- (2) or uni- (1) directional.
+            Default: 2
         :type frame_size: str or int
         :type traffic_profile: str
         :type minimum_transmit_rate: float
@@ -728,16 +742,20 @@ class OptimizedSearch(object):
         :type number_of_intermediate_phases: int
         :type timeout: float
         :type doublings: int
+        :type traffic_directions: int
         :returns: Structure containing narrowed down NDR and PDR intervals
             and their measurements.
         :rtype: NdrPdrResult
         :raises RuntimeError: If total duration is larger than timeout.
         """
+        minimum_transmit_rate *= traffic_directions
+        maximum_transmit_rate *= traffic_directions
         # we need instance of TrafficGenerator instantiated by Robot Framework
         # to be able to use trex_stl-*()
         tg_instance = BuiltIn().get_library_instance(
             'resources.libraries.python.TrafficGenerator')
-        tg_instance.set_rate_provider_defaults(frame_size, traffic_profile)
+        tg_instance.set_rate_provider_defaults(
+            frame_size, traffic_profile, traffic_directions=traffic_directions)
         algorithm = MultipleLossRatioSearch(
             measurer=tg_instance, final_trial_duration=final_trial_duration,
             final_relative_width=final_relative_width,
@@ -752,15 +770,16 @@ class OptimizedSearch(object):
     def perform_soak_search(
             frame_size, traffic_profile, minimum_transmit_rate,
             maximum_transmit_rate, plr_target=1e-7, tdpt=0.1,
-            initial_count=50, timeout=1800.0, trace_enabled=False):
+            initial_count=50, timeout=1800.0, trace_enabled=False,
+            traffic_directions=2):
         """Setup initialized TG, perform soak search, return avg and stdev.
 
         :param frame_size: Frame size identifier or value [B].
         :param traffic_profile: Module name as a traffic profile identifier.
             See resources/traffic_profiles/trex for implemented modules.
-        :param minimum_transmit_rate: Minimal bidirectional
+        :param minimum_transmit_rate: Minimal uni-directional
             target transmit rate [pps].
-        :param maximum_transmit_rate: Maximal bidirectional
+        :param maximum_transmit_rate: Maximal uni-directional
             target transmit rate [pps].
         :param plr_target: Fraction of packets lost to achieve [1].
         :param tdpt: Trial duration per trial.
@@ -771,6 +790,8 @@ class OptimizedSearch(object):
             This is needed because initial "search" phase of integrator
             takes significant time even without any trial results.
         :param timeout: The search will stop after this overall time [s].
+        :param traffic_directions: Traffic is bi- (2) or uni- (1) directional.
+            Default: 2
         :type frame_size: str or int
         :type traffic_profile: str
         :type minimum_transmit_rate: float
@@ -778,12 +799,16 @@ class OptimizedSearch(object):
         :type plr_target: float
         :type initial_count: int
         :type timeout: float
-        :returns: Average and stdev of estimated bidirectional rate giving PLR.
+        :type traffic_directions: int
+        :returns: Average and stdev of estimated aggregate rate giving PLR.
         :rtype: 2-tuple of float
         """
+        minimum_transmit_rate *= traffic_directions
+        maximum_transmit_rate *= traffic_directions
         tg_instance = BuiltIn().get_library_instance(
             'resources.libraries.python.TrafficGenerator')
-        tg_instance.set_rate_provider_defaults(frame_size, traffic_profile)
+        tg_instance.set_rate_provider_defaults(
+            frame_size, traffic_profile, traffic_directions=traffic_directions)
         algorithm = PLRsearch(
             measurer=tg_instance, trial_duration_per_trial=tdpt,
             packet_loss_ratio_target=plr_target,
index 8ac7155..198147b 100644 (file)
@@ -36,7 +36,7 @@
 | | ... | Display findings as a formatted test message.
 | | ... | Fail if a resulting lower bound has too high loss fraction.
 | | ... | Input rates are understood as uni-directional,
-| | ... | reported result contains bi-directional rates.
+| | ... | reported result contains aggregate rates.
 | | ... | Currently, the min_rate value is hardcoded to match test teardowns.
 | | ...
 | | ... | TODO: Should the trial duration of the additional
 | | ... | - intermediate phases - Number of intermediate phases [1]. Type: int
 | | ... | - timeout - Fail if search duration is longer [s]. Type: float
 | | ... | - doublings - How many doublings to do when expanding [1]. Type: int
+| | ... | - traffic_directions - Bi- (2) or uni- (1) directional traffic.
+| | ... | Type: int
 | | ...
 | | ... | *Example:*
 | | ...
 | | ... | \| Find NDR and PDR intervals using optimized search \| \${0.005} \
-| | ... | \| \${0.005} \| \${30.0} \| \${1.0} \| \${2} \| ${600.0} \| ${2} \|
+| | ... | \| \${0.005} \| \${30.0} \| \${1.0} \| \${2} \| \${600.0} \| \${2} \
+| | ... | \| \${2} \|
 | | ...
 | | [Arguments] | ${packet_loss_ratio}=${0.005}
 | | ... | ${final_relative_width}=${0.005} | ${final_trial_duration}=${30.0}
 | | ... | ${initial_trial_duration}=${1.0}
 | | ... | ${number_of_intermediate_phases}=${2} | ${timeout}=${720.0}
-| | ... | ${doublings}=${2}
+| | ... | ${doublings}=${2} | ${traffic_directions}=${2}
 | | ...
 | | ${result} = | Perform optimized ndrpdr search | ${frame_size}
-| | ... | ${traffic_profile} | ${20000} | ${max_rate*2}
+| | ... | ${traffic_profile} | ${10000} | ${max_rate}
 | | ... | ${packet_loss_ratio} | ${final_relative_width}
 | | ... | ${final_trial_duration} | ${initial_trial_duration}
 | | ... | ${number_of_intermediate_phases} | timeout=${timeout}
-| | ... | doublings=${doublings}
+| | ... | doublings=${doublings} | traffic_directions=${traffic_directions}
 | | Display result of NDRPDR search | ${result}
 | | Check NDRPDR interval validity | ${result.pdr_interval}
 | | ... | ${packet_loss_ratio}
 | Find Throughput Using MLRsearch
 | | [Documentation]
 | | ... | Find and return lower bound PDR (zero PLR by default)
-| | ... | bi-directional throughput using MLRsearch algorithm.
-| | ... | Input rates are understood as uni-directional,
-| | ... | reported result contains bi-directional rates.
+| | ... | aggregate throughput using MLRsearch algorithm.
+| | ... | Input rates are understood as uni-directional.
 | | ... | Currently, the min_rate value is hardcoded to match test teardowns.
 | | ...
 | | ... | TODO: Should the trial duration of the additional
 | | ... | - intermediate phases - Number of intermediate phases [1]. Type: int
 | | ... | - timeout - Fail if search duration is longer [s]. Type: float
 | | ... | - doublings - How many doublings to do when expanding [1]. Type: int
+| | ... | - traffic_directions - Bi- (2) or uni- (1) directional traffic.
+| | ... | Type: int
 | | ...
 | | ... | *Returns:*
 | | ... | - Lower bound for bi-directional throughput at given PLR. Type: float
 | | ... | *Example:*
 | | ...
 | | ... | \| \${throughpt}= \| Find Throughput Using MLRsearch \| \${0} \
-| | ... | \| \${0.001} \| \${10.0}\| \${1.0} \| \${1} \| ${720.0} \| ${2} \|
+| | ... | \| \${0.001} \| \${10.0}\| \${1.0} \| \${1} \| \${720.0} \| \${2} \
+| | ... | \| \${2} \|
 | | ...
 | | [Arguments] | ${packet_loss_ratio}=${0.0}
 | | ... | ${final_relative_width}=${0.001} | ${final_trial_duration}=${10.0}
 | | ... | ${initial_trial_duration}=${1.0}
 | | ... | ${number_of_intermediate_phases}=${1} | ${timeout}=${720.0}
-| | ... | ${doublings}=${2}
+| | ... | ${doublings}=${2} | ${traffic_directions}=${2}
 | | ...
 | | ${result} = | Perform optimized ndrpdr search | ${frame_size}
-| | ... | ${traffic_profile} | ${20000} | ${max_rate*2}
+| | ... | ${traffic_profile} | ${10000} | ${max_rate}
 | | ... | ${packet_loss_ratio} | ${final_relative_width}
 | | ... | ${final_trial_duration} | ${initial_trial_duration}
 | | ... | ${number_of_intermediate_phases} | timeout=${timeout}
-| | ... | doublings=${doublings}
+| | ... | doublings=${doublings} | traffic_directions=${traffic_directions}
 | | Return From Keyword | ${result.pdr_interval.measured_low.transmit_rate}
 
 | Find critical load using PLRsearch
 | | ... | Display results as formatted test message.
 | | ... | Fail if computed lower bound is below minimal rate.
 | | ... | Input rates are understood as uni-directional,
-| | ... | reported result contains bi-directional rates.
+| | ... | reported result contains aggregate rates.
 | | ... | Currently, the min_rate value is hardcoded to match test teardowns.
 | | ... | Some inputs are read from variables to streamline suites.
 | | ...
 | | ... | *Arguments:*
 | | ... | - packet_loss_ratio - Accepted loss during search. Type: float
 | | ... | - timeout - Stop when search duration is longer [s]. Type: float
+| | ... | - traffic_directions - Bi- (2) or uni- (1) directional traffic.
+| | ... | Type: int
 | | ...
 | | ... | *Example:*
 | | ...
-| | ... | \| Find critical load using PLR search \| \${1e-7} \| \${120} \|
+| | ... | \| Find critical load using PLR search \| \${1e-7} \| \${120} \
+| | ... | \| \${2} \|
 | | ...
 | | [Arguments] | ${packet_loss_ratio}=${1e-7} | ${timeout}=${1800.0}
+| | ... | ${traffic_directions}=${2}
 | | ...
-| | ${min_rate} = | Set Variable | ${20000}
+| | ${min_rate} = | Set Variable | ${10000}
 | | ${average} | ${stdev} = | Perform soak search | ${frame_size}
-| | ... | ${traffic_profile} | ${min_rate} | ${max_rate*2}
+| | ... | ${traffic_profile} | ${min_rate} | ${max_rate}
 | | ... | ${packet_loss_ratio} | timeout=${timeout}
+| | ... | traffic_directions=${traffic_directions}
 | | ${lower} | ${upper} = | Display result of soak search
 | | ... | ${average} | ${stdev}
 | | Should Not Be True | ${lower} < ${min_rate}
-| | ... | Lower bound ${lower} is below bidirectional minimum ${min_rate}.
+| | ... | Lower bound ${lower} is below unidirectional minimum ${min_rate}.
 
 | Display single bound
 | | [Documentation]
 | | ... | Display one bound of NDR+PDR search,
 | | ... | in packet per seconds (total and per stream)
-| | ... | and Gbps total bandwidth with untagged packet.
+| | ... | and Gbps total bandwidth (for initial packet size).
 | | ... | Througput is calculated as:
-| | ... | Measured rate per stream * Total number of streams
+| | ... | Sum of measured rates over streams
 | | ... | Bandwidth is calculated as:
-| | ... | (Throughput * (L2 Frame Size + IPG) * 8) / Max bitrate of NIC
+| | ... | (Throughput * (L2 Frame Size + IPG) * 8)
 | | ... | The given result should contain latency data as well.
 | | ...
 | | ... | *Arguments:*
 | | ...
 | | ${bandwidth_total} = | Evaluate | ${rate_total} * (${frame_size}+20)*8 / 1e9
 | | Set Test Message | ${\n}${text}: ${rate_total} pps, | append=yes
-| | Set Test Message | ${bandwidth_total} Gbps (untagged) | append=yes
+| | Set Test Message | ${bandwidth_total} Gbps (initial) | append=yes
 | | Return From Keyword If | not """${latency}"""
 | | Set Test Message | ${\n}LATENCY usec [min/avg/max] per stream: ${latency}
 | | ... | append=yes
 | Display result of NDRPDR search
 | | [Documentation]
 | | ... | Display result of NDR+PDR search, both quantities, both bounds,
-| | ... | in packet per seconds (total and per stream)
-| | ... | and Gbps total bandwidth with untagged packet.
+| | ... | aggregate in packet per seconds
+| | ... | and Gbps total bandwidth (for initial packet size).
 | | ... | Througput is calculated as:
-| | ... | Measured rate per stream * Total number of streams
+| | ... | Sum of measured rate over streams
 | | ... | Bandwidth is calculated as:
-| | ... | (Throughput * (L2 Frame Size + IPG) * 8) / Max bitrate of NIC
+| | ... | (Throughput * (L2 Frame Size + IPG) * 8)
 | | ... | The given result should contain latency data as well.
 | | ...
 | | ... | *Test (or broader scope) variables read:*
 | Display result of soak search
 | | [Documentation]
 | | ... | Display result of soak search, avg+-stdev, as upper/lower bounds,
-| | ... | in packet per seconds (total and per stream)
-| | ... | and Gbps total bandwidth with untagged packet.
+| | ... | in aggregate packets per seconds
+| | ... | and Gbps total bandwidth (for initial packet size).
 | | ... | Througput is calculated as:
-| | ... | Measured rate per stream * Total number of streams
+| | ... | Sum of measured rates over streams
 | | ... | Bandwidth is calculated as:
-| | ... | (Throughput * (L2 Frame Size + IPG) * 8) / Max bitrate of NIC
+| | ... | (Throughput * (L2 Frame Size + IPG) * 8)
 | | ... | TODO: Do we want to report some latency data,
 | | ... | even if not measured at the reported bounds?.
 | | ...
 | | ${stdev} = | Convert To Number | ${stdev}
 | | ${lower} = | Evaluate | ${avg} - ${stdev}
 | | ${upper} = | Evaluate | ${avg} + ${stdev}
-| | Display single bound | PLRsearch lower bound: | ${lower} | ${frame_size}
-| | Display single bound | PLRsearch upper bound: | ${upper} | ${frame_size}
+| | Display single bound | PLRsearch lower bound | ${lower} | ${frame_size}
+| | Display single bound | PLRsearch upper bound | ${upper} | ${frame_size}
 | | Return From Keyword | ${lower} | ${upper}
 
 | Check NDRPDR interval validity
 | | ... | - result - Measured result data per stream [pps]. Type: NdrPdrResult
 | | ... | - frame_size - L2 Frame Size [B] or IMIX string. Type: int or str
 | | ... | - traffic_profile - Topology profile. Type: string
+| | ... | - traffic_directions - Bi- (2) or uni- (1) directional traffic.
+| | ... | Type: int
 | | ...
 | | ... | *Example:*
 | | ... | \| Perform additional measurements based on NDRPDR result \
-| | ... | \| \${result} \| ${64} \| 3-node-IPv4 \|
+| | ... | \| \${result} \| \${64} \| 3-node-IPv4 \| \${2} \|
 | | ...
 | | [Arguments] | ${result} | ${framesize} | ${traffic_profile}
+| | ... | ${traffic_directions}=${2}
 | | ...
-| | ${duration}= | Set Variable | 2.0
-| | ${rate_per_stream}= | Evaluate
-| | ... | ${result.ndr_interval.measured_low.target_tr} / 2.0
+| | ${duration}= | Set Variable | ${2.0}
+| | ${rate_sum}= | Set Variable | ${result.ndr_interval.measured_low.target_tr}
+| | ${rate_per_stream}= | Evaluate | ${rate_sum} / float(${traffic_directions})
 | | Traffic should pass with no loss | ${duration} | ${rate_per_stream}pps
 | | ... | ${framesize} | ${traffic_profile} | fail_on_loss=${False}
+| | ... | traffic_directions=${traffic_directions}
 
 | Traffic should pass with no loss
 | | [Documentation]
 | | ... | Type: string
 | | ... | - fail_on_loss - If True, the keyword fails if loss occurred.
 | | ... | Type: boolean
+| | ... | - traffic_directions - Bi- (2) or uni- (1) directional traffic.
+| | ... | Type: int
 | | ...
 | | ... | *Example:*
 | | ...
-| | ... | \| Traffic should pass with no loss \| 10 \| 4.0mpps \| 64 \
-| | ... | \| 3-node-IPv4 \|
+| | ... | \| Traffic should pass with no loss \| \${10} \| 4.0mpps \| \${64} \
+| | ... | \| 3-node-IPv4 \| \${2} \|
 | | ...
 | | [Arguments] | ${duration} | ${rate} | ${frame_size} | ${traffic_profile}
-| | ... | ${fail_on_loss}=${True}
+| | ... | ${fail_on_loss}=${True} | ${traffic_directions}=${2}
 | | ...
 | | Send traffic at specified rate | ${duration} | ${rate} | ${frame_size}
-| | ... | ${traffic_profile}
+| | ... | ${traffic_profile} | traffic_directions=${traffic_directions}
 | | Run Keyword If | ${fail_on_loss} | No traffic loss occurred
 
 | Traffic should pass with maximum rate
 | | ... | - subsamples - How many trials in this measurement. Type: int
 | | ... | - trial_duration - Duration of single trial [s]. Type: float
 | | ... | - fail_no_traffic - Whether to fail on zero receive count. Type: boolean
-| | ... | - unidirection - False if traffic is bidirectional. Type: boolean
+| | ... | - traffic_directions - Bi- (2) or uni- (1) directional traffic.
+| | ... | Type: int
 | | ... | - tx_port - TX port of TG, default 0. Type: integer
 | | ... | - rx_port - RX port of TG, default 1. Type: integer
 | | ...
 | | ... | *Example:*
 | | ...
-| | ... | \| Traffic should pass with maximum rate \| ${1} \| ${10.0} \
-| | ... | \| ${False} \| ${False} \| ${0} \| ${1} \|
+| | ... | \| Traffic should pass with maximum rate \| \${1} \| \${10.0} \
+| | ... | \| \${False} \| \${2} \| \${0} \| \${1} \|
 | | ...
 | | [Arguments] | ${trial_duration}=${PERF_TRIAL_DURATION}
 | | ... | ${fail_no_traffic}=${True} | ${subsamples}=${PERF_TRIAL_MULTIPLICITY}
-| | ... | ${unidirection}=${False} | ${tx_port}=${0} | ${rx_port}=${1}
+| | ... | ${traffic_directions}=${2} | ${tx_port}=${0} | ${rx_port}=${1}
 | | ...
 | | ${results} = | Send traffic at specified rate | ${trial_duration}
 | | ... | ${max_rate}pps | ${frame_size} | ${traffic_profile} | ${subsamples}
-| | ... | ${unidirection} | ${tx_port} | ${rx_port}
+| | ... | ${traffic_directions} | ${tx_port} | ${rx_port}
 | | Set Test Message | ${\n}Maximum Receive Rate trial results
 | | Set Test Message | in packets per second: ${results}
 | | ... | append=yes
 | | ... | - traffic_profile - Name of module defining traffc for measurements.
 | | ... | Type: string
 | | ... | - subsamples - How many trials in this measurement. Type: int
-| | ... | - unidirection - False if traffic is bidirectional. Type: boolean
+| | ... | - traffic_directions - Bi- (2) or uni- (1) directional traffic.
+| | ... | Type: int
 | | ... | - tx_port - TX port of TG, default 0. Type: integer
 | | ... | - rx_port - RX port of TG, default 1. Type: integer
 | | ... | - pkt_trace - True to enable packet trace. Type: boolean
 | | ...
 | | ... | *Example:*
 | | ...
-| | ... | \| Send traffic at specified rate \| ${1.0} \| 4.0mpps \| 64 \
-| | ... | \| 3-node-IPv4 \| ${10} \| ${False} \| ${0} | ${1} \| ${False}
+| | ... | \| Send traffic at specified rate \| \${1.0} \| 4.0mpps \| \${64} \
+| | ... | \| 3-node-IPv4 \| \${10} \| \${2} \| \${0} | \${1} \| \${False}
 | | ...
 | | [Arguments] | ${trial_duration} | ${rate} | ${frame_size}
-| | ... | ${traffic_profile} | ${subsamples}=${1} | ${unidirection}=${False}
+| | ... | ${traffic_profile} | ${subsamples}=${1} | ${traffic_directions}=${2}
 | | ... | ${tx_port}=${0} | ${rx_port}=${1} | ${pkt_trace}=${False}
 | | ...
 | | Clear and show runtime counters with running traffic | ${trial_duration}
 | | ... | ${rate} | ${frame_size} | ${traffic_profile}
-| | ... | ${unidirection} | ${tx_port} | ${rx_port}
+| | ... | ${traffic_directions} | ${tx_port} | ${rx_port}
 | | Run Keyword If | ${dut_stats}==${True}
 | | ... | Clear statistics on all DUTs | ${nodes}
 | | Run Keyword If | ${dut_stats}==${True} and ${pkt_trace}==${True}
 | | | # The following line is skipping some default arguments,
 | | | # that is why subsequent arguments have to be named.
 | | | Send traffic on tg | ${trial_duration} | ${rate} | ${frame_size}
-| | | ... | ${traffic_profile} | warmup_time=${0} | unidirection=${unidirection}
-| | | ... | tx_port=${tx_port} | rx_port=${rx_port}
+| | | ... | ${traffic_profile} | warmup_time=${0}
+| | | ... | traffic_directions=${traffic_directions} | tx_port=${tx_port}
+| | | ... | rx_port=${rx_port}
 | | | ${rx} = | Get Received
 | | | ${rr} = | Evaluate | ${rx} / ${trial_duration}
 | | | Append To List | ${results} | ${rr}
 | | ... | - frame_size - L2 Frame Size [B] or IMIX_v4_1. Type: integer/string
 | | ... | - traffic_profile - Name of module defining traffc for measurements.
 | | ... | Type: string
-| | ... | - unidirection - False if traffic is bidirectional. Type: boolean
+| | ... | - traffic_directions - Bi- (2) or uni- (1) directional traffic.
+| | ... | Type: int
 | | ... | - tx_port - TX port of TG, default 0. Type: integer
 | | ... | - rx_port - RX port of TG, default 1. Type: integer
 | | ...
 | | ... | *Example:*
 | | ...
-| | ... | \| Clear and show runtime counters with running traffic \| 10 \
-| | ... | \| 4.0mpps \| 64 \| 3-node-IPv4 \| ${False} \| ${0} | ${1} \|
+| | ... | \| Clear and show runtime counters with running traffic \| \${10} \
+| | ... | \| 4.0mpps \| \${64} \| 3-node-IPv4 \| \${2} \| \${0} \| \${1} \|
 | | ...
 | | [Arguments] | ${duration} | ${rate} | ${frame_size} | ${traffic_profile}
-| | ... | ${unidirection}=${False} | ${tx_port}=${0} | ${rx_port}=${1}
+| | ... | ${traffic_directions}=${2} | ${tx_port}=${0} | ${rx_port}=${1}
 | | ...
 | | # Duration of -1 means we will stop traffic manually.
 | | Send traffic on tg | ${-1} | ${rate} | ${frame_size} | ${traffic_profile}
 | | ... | warmup_time=${0} | async_call=${True} | latency=${False}
-| | ... | unidirection=${unidirection} | tx_port=${tx_port} | rx_port=${rx_port}
+| | ... | traffic_directions=${traffic_directions} | tx_port=${tx_port}
+| | ... | rx_port=${rx_port}
 | | Run Keyword If | ${dut_stats}==${True}
 | | ... | VPP clear runtime counters on all DUTs | ${nodes}
 | | Sleep | ${duration}
 | | ... | - frame_size - L2 Frame Size [B] or IMIX string. Type: int or str
 | | ... | *Arguments:*
 | | ... | - rate - Unidirectional rate for sending packets. Type: string
-| | ... | - unidirection - False if traffic is bidirectional. Type: boolean
+| | ... | - traffic_directions - Bi- (2) or uni- (1) directional traffic.
+| | ... | Type: int
 | | ... | - tx_port - TX port of TG, default 0. Type: integer
 | | ... | - rx_port - RX port of TG, default 1. Type: integer
 | | ...
 | | ... | *Example:*
 | | ...
-| | ... | \| Start Traffic on Background \| 4.0mpps \| ${False} \| ${0} \
-| | ... | \| ${1} \|
+| | ... | \| Start Traffic on Background \| 4.0mpps \| \${2} \| \${0} \
+| | ... | \| \${1} \|
 | | ...
-| | [Arguments] | ${rate} | ${unidirection}=${False} | ${tx_port}=${0}
+| | [Arguments] | ${rate} | ${traffic_directions}=${2} | ${tx_port}=${0}
 | | ... | ${rx_port}=${1}
 | | ...
 | | # Duration of -1 means we will stop traffic manually.
 | | Send traffic on tg | ${-1} | ${rate} | ${frame_size} | ${traffic_profile}
 | | ... | warmup_time=${0} | async_call=${True} | latency=${False}
-| | ... | unidirection=${unidirection} | tx_port=${tx_port} | rx_port=${rx_port}
+| | ... | traffic_directions=${traffic_directions} | tx_port=${tx_port}
+| | ... | rx_port=${rx_port}
 
 | Stop Running Traffic
 | | [Documentation]
index 24a24f6..670cb32 100644 (file)
@@ -251,8 +251,8 @@ class ExecutionChecker(ResultVisitor):
     # TODO: Remove when definitely no NDRPDRDISC tests are used:
     REGEX_RATE = re.compile(r'^[\D\d]*FINAL_RATE:\s(\d+\.\d+)\s(\w+)')
 
-    REGEX_PLR_RATE = re.compile(r'PLRsearch lower bound::\s(\d+.\d+).*\n'
-                                r'PLRsearch upper bound::\s(\d+.\d+)')
+    REGEX_PLR_RATE = re.compile(r'PLRsearch lower bound::?\s(\d+.\d+).*\n'
+                                r'PLRsearch upper bound::?\s(\d+.\d+)')
 
     REGEX_NDRPDR_RATE = re.compile(r'NDR_LOWER:\s(\d+.\d+).*\n.*\n'
                                    r'NDR_UPPER:\s(\d+.\d+).*\n'
index b8ee77c..2837778 100755 (executable)
@@ -56,7 +56,7 @@ def fmt_latency(lat_min, lat_avg, lat_max):
 
 
 def simple_burst(profile_file, duration, framesize, rate, warmup_time, port_0,
-                 port_1, latency, async_start=False, unidirection=False):
+                 port_1, latency, async_start=False, traffic_directions=2):
     """Send traffic and measure packet loss and latency.
 
     Procedure:
@@ -83,7 +83,7 @@ def simple_burst(profile_file, duration, framesize, rate, warmup_time, port_0,
     :param port_1: Port 1 on the traffic generator.
     :param latency: With latency stats.
     :param async_start: Start the traffic and exit.
-    :param unidirection: Traffic is unidirectional.
+    :param traffic_directions: Bidirectional (2) or unidirectional (1) traffic.
     :type profile_file: str
     :type framesize: int or str
     :type duration: float
@@ -93,7 +93,7 @@ def simple_burst(profile_file, duration, framesize, rate, warmup_time, port_0,
     :type port_1: int
     :type latency: bool
     :type async_start: bool
-    :type unidirection: bool
+    :type traffic_directions: int
     """
     client = None
     total_rcvd = 0
@@ -126,17 +126,17 @@ def simple_burst(profile_file, duration, framesize, rate, warmup_time, port_0,
             client.set_port_attr(ports=[port_0, port_1], promiscuous=True)
         if isinstance(framesize, int):
             client.add_streams(streams[0], ports=[port_0])
-            if not unidirection:
+            if traffic_directions > 1:
                 client.add_streams(streams[1], ports=[port_1])
         elif isinstance(framesize, str):
             client.add_streams(streams[0:3], ports=[port_0])
-            if not unidirection:
+            if traffic_directions > 1:
                 client.add_streams(streams[3:6], ports=[port_1])
         if latency:
             try:
                 if isinstance(framesize, int):
                     client.add_streams(streams[2], ports=[port_0])
-                    if not unidirection:
+                    if traffic_directions > 1:
                         client.add_streams(streams[3], ports=[port_1])
                 elif isinstance(framesize, str):
                     latency = False
@@ -145,7 +145,7 @@ def simple_burst(profile_file, duration, framesize, rate, warmup_time, port_0,
                 print("##### FAILED to add latency streams #####")
                 latency = False
         ports = [port_0]
-        if not unidirection:
+        if traffic_directions > 1:
             ports.append(port_1)
         # Warm-up phase:
         if warmup_time > 0:
@@ -169,12 +169,12 @@ def simple_burst(profile_file, duration, framesize, rate, warmup_time, port_0,
             print(json.dumps(stats, indent=4, separators=(',', ': ')))
 
             lost_a = stats[port_0]["opackets"] - stats[port_1]["ipackets"]
-            if not unidirection:
+            if traffic_directions > 1:
                 lost_b = stats[port_1]["opackets"] - stats[port_0]["ipackets"]
 
             print("\npackets lost from {p_0} --> {p_1}: {v} pkts".format(
                 p_0=port_0, p_1=port_1, v=lost_a))
-            if not unidirection:
+            if traffic_directions > 1:
                 print("packets lost from {p_1} --> {p_0}: {v} pkts".format(
                     p_0=port_0, p_1=port_1, v=lost_b))
 
@@ -201,21 +201,23 @@ def simple_burst(profile_file, duration, framesize, rate, warmup_time, port_0,
             print(json.dumps(stats, indent=4, separators=(',', ': ')))
 
             lost_a = stats[port_0]["opackets"] - stats[port_1]["ipackets"]
-            if not unidirection:
+            if traffic_directions > 1:
                 lost_b = stats[port_1]["opackets"] - stats[port_0]["ipackets"]
 
+            # Stats index is not a port number, but "pgid".
+            # TODO: Find out what "pgid" means.
             if latency:
                 lat_a = fmt_latency(
-                    str(stats["latency"][port_0]["latency"]["total_min"]),
-                    str(stats["latency"][port_0]["latency"]["average"]),
-                    str(stats["latency"][port_0]["latency"]["total_max"]))
-                if not unidirection:
+                    str(stats["latency"][0]["latency"]["total_min"]),
+                    str(stats["latency"][0]["latency"]["average"]),
+                    str(stats["latency"][0]["latency"]["total_max"]))
+                if traffic_directions > 1:
                     lat_b = fmt_latency(
-                        str(stats["latency"][port_1]["latency"]["total_min"]),
-                        str(stats["latency"][port_1]["latency"]["average"]),
-                        str(stats["latency"][port_1]["latency"]["total_max"]))
+                        str(stats["latency"][1]["latency"]["total_min"]),
+                        str(stats["latency"][1]["latency"]["average"]),
+                        str(stats["latency"][1]["latency"]["total_max"]))
 
-            if not unidirection:
+            if traffic_directions > 1:
                 total_sent = stats[0]["opackets"] + stats[1]["opackets"]
                 total_rcvd = stats[0]["ipackets"] + stats[1]["ipackets"]
             else:
@@ -224,7 +226,7 @@ def simple_burst(profile_file, duration, framesize, rate, warmup_time, port_0,
 
             print("\npackets lost from {p_0} --> {p_1}:   {v} pkts".format(
                 p_0=port_0, p_1=port_1, v=lost_a))
-            if not unidirection:
+            if traffic_directions > 1:
                 print("packets lost from {p_1} --> {p_0}:   {v} pkts".format(
                 p_0=port_0, p_1=port_1, v=lost_b))
 
@@ -287,10 +289,10 @@ def main():
                         action="store_true",
                         default=False,
                         help="Add latency stream.")
-    parser.add_argument("--unidirection",
-                        action="store_true",
-                        default=False,
-                        help="Send unidirection traffic.")
+    parser.add_argument("--traffic_directions",
+                        type=int,
+                        default=2,
+                        help="Send bi- (2) or uni- (1) directional traffic.")
 
     args = parser.parse_args()
 
@@ -308,7 +310,7 @@ def main():
                  port_1=args.port_1,
                  latency=args.latency,
                  async_start=args.async,
-                 unidirection=args.unidirection)
+                 traffic_directions=args.traffic_directions)
 
 
 if __name__ == '__main__':