+ command_line = OptionString().add("python3")
+ dirname = f"{Constants.REMOTE_FW_DIR}/GPL/tools/trex"
+ command_line.add(f"'{dirname}/trex_stl_stop.py'")
+ command_line.add("--xstat")
+ for value in self._xstats:
+ if value is not None:
+ value = value.replace("'", "\"")
+ command_line.add(f"'{value}'")
+ stdout, _ = exec_cmd_no_error(
+ node, command_line,
+ message="T-Rex STL runtime error!"
+ )
+ self._parse_traffic_results(stdout)
+
+ def stop_traffic_on_tg(self):
+ """Stop all traffic on TG.
+
+ :returns: Structure containing the result of the measurement.
+ :rtype: MeasurementResult
+ :raises ValueError: If TG traffic profile is not supported.
+ """
+ subtype = check_subtype(self._node)
+ if subtype != NodeSubTypeTG.TREX:
+ raise ValueError(f"Unsupported TG subtype: {subtype!r}")
+ if u"trex-astf" in self.traffic_profile:
+ self.trex_astf_stop_remote_exec(self._node)
+ elif u"trex-stl" in self.traffic_profile:
+ self.trex_stl_stop_remote_exec(self._node)
+ else:
+ raise ValueError(u"Unsupported T-Rex traffic profile!")
+ self._stop_time = time.monotonic()
+
+ return self._get_measurement_result()
+
+ def _compute_duration(self, duration, multiplier):
+ """Compute duration for profile driver.
+
+ The final result is influenced by transaction scale and duration limit.
+ It is assumed a higher level function has already set those on self.
+ The duration argument is the target value from search point of view,
+ before the overrides are applied here.
+
+ Minus one (signalling async traffic start) is kept.
+
+ Completeness flag is also included. Duration limited or async trials
+ are not considered complete for ramp-up purposes.
+
+ :param duration: Time expressed in seconds for how long to send traffic.
+ :param multiplier: Traffic rate in transactions per second.
+ :type duration: float
+ :type multiplier: float
+ :returns: New duration and whether it was a complete ramp-up candidate.
+ :rtype: float, bool
+ """
+ if duration < 0.0:
+ # Keep the async -1.
+ return duration, False
+ computed_duration = duration
+ if self.transaction_scale:
+ computed_duration = self.transaction_scale / multiplier
+ # Log the computed duration,
+ # so we can compare with what telemetry suggests
+ # the real duration was.
+ logger.debug(f"Expected duration {computed_duration}")
+ if not self.duration_limit:
+ return computed_duration, True
+ limited_duration = min(computed_duration, self.duration_limit)
+ return limited_duration, (limited_duration == computed_duration)
+
+ def trex_astf_start_remote_exec(
+ self, duration, multiplier, async_call=False):
+ """Execute T-Rex ASTF script on remote node over ssh to start running
+ traffic.
+
+ In sync mode, measurement results are stored internally.
+ In async mode, initial data including xstats are stored internally.
+
+ This method contains the logic to compute duration as maximum time
+ if transaction_scale is nonzero.
+ The transaction_scale argument defines (limits) how many transactions
+ will be started in total. As that amount of transaction can take
+ considerable time (sometimes due to explicit delays in the profile),
+ the real time a trial needs to finish is computed here. For now,
+ in that case the duration argument is ignored, assuming it comes
+ from ASTF-unaware search algorithm. The overall time a single
+ transaction needs is given in parameter transaction_duration,
+ it includes both explicit delays and implicit time it takes
+ to transfer data (or whatever the transaction does).
+
+ Currently it is observed TRex does not start the ASTF traffic
+ immediately, an ad-hoc constant is added to the computed duration
+ to compensate for that.
+
+ If transaction_scale is zero, duration is not recomputed.
+ It is assumed the subsequent result parsing gets the real duration
+ if the traffic stops sooner for any reason.
+
+ Currently, it is assumed traffic profile defines a single transaction.
+ To avoid heavy logic here, the input rate is expected to be in
+ transactions per second, as that directly translates to TRex multiplier,
+ (assuming the profile does not override the default cps value of one).
+
+ :param duration: Time expressed in seconds for how long to send traffic.
+ :param multiplier: Traffic rate in transactions per second.
+ :param async_call: If enabled then don't wait for all incoming traffic.
+ :type duration: float
+ :type multiplier: int
+ :type async_call: bool
+ :raises RuntimeError: In case of T-Rex driver issue.
+ """
+ self.check_mode(TrexMode.ASTF)
+ p_0, p_1 = (1, 0) if self._ifaces_reordered else (0, 1)
+ if not isinstance(duration, (float, int)):
+ duration = float(duration)
+
+ computed_duration, _ = self._compute_duration(duration, multiplier)
+
+ command_line = OptionString().add(u"python3")
+ dirname = f"{Constants.REMOTE_FW_DIR}/GPL/tools/trex"
+ command_line.add(f"'{dirname}/trex_astf_profile.py'")
+ command_line.change_prefix(u"--")
+ dirname = f"{Constants.REMOTE_FW_DIR}/GPL/traffic_profiles/trex"
+ command_line.add_with_value(
+ u"profile", f"'{dirname}/{self.traffic_profile}.py'"
+ )
+ command_line.add_with_value(u"duration", f"{computed_duration!r}")
+ command_line.add_with_value(u"frame_size", self.frame_size)
+ command_line.add_with_value(
+ u"n_data_frames", Constants.ASTF_N_DATA_FRAMES
+ )
+ command_line.add_with_value(u"multiplier", multiplier)
+ command_line.add_with_value(u"port_0", p_0)
+ command_line.add_with_value(u"port_1", p_1)
+ command_line.add_with_value(
+ u"traffic_directions", self.traffic_directions
+ )
+ command_line.add_if(u"async_start", async_call)
+ command_line.add_if(u"latency", self.use_latency)
+ command_line.add_if(u"force", Constants.TREX_SEND_FORCE)
+ command_line.add_with_value(
+ u"delay", Constants.PERF_TRIAL_ASTF_DELAY
+ )
+
+ self._start_time = time.monotonic()
+ self._rate = multiplier
+ stdout, _ = exec_cmd_no_error(
+ self._node, command_line, timeout=computed_duration + 10.0,
+ message=u"T-Rex ASTF runtime error!"
+ )
+
+ if async_call:
+ # no result
+ self._target_duration = None
+ self._duration = None
+ self._received = None
+ self._sent = None
+ self._loss = None
+ self._latency = None
+ xstats = []
+ self._l7_data = dict()
+ self._l7_data[u"client"] = dict()
+ self._l7_data[u"client"][u"active_flows"] = None
+ self._l7_data[u"client"][u"established_flows"] = None
+ self._l7_data[u"client"][u"traffic_duration"] = None
+ self._l7_data[u"server"] = dict()
+ self._l7_data[u"server"][u"active_flows"] = None
+ self._l7_data[u"server"][u"established_flows"] = None
+ self._l7_data[u"server"][u"traffic_duration"] = None
+ if u"udp" in self.traffic_profile:
+ self._l7_data[u"client"][u"udp"] = dict()
+ self._l7_data[u"client"][u"udp"][u"connects"] = None
+ self._l7_data[u"client"][u"udp"][u"closed_flows"] = None
+ self._l7_data[u"client"][u"udp"][u"err_cwf"] = None
+ self._l7_data[u"server"][u"udp"] = dict()
+ self._l7_data[u"server"][u"udp"][u"accepted_flows"] = None
+ self._l7_data[u"server"][u"udp"][u"closed_flows"] = None
+ elif u"tcp" in self.traffic_profile:
+ self._l7_data[u"client"][u"tcp"] = dict()
+ self._l7_data[u"client"][u"tcp"][u"initiated_flows"] = None
+ self._l7_data[u"client"][u"tcp"][u"connects"] = None
+ self._l7_data[u"client"][u"tcp"][u"closed_flows"] = None
+ self._l7_data[u"client"][u"tcp"][u"connattempt"] = None
+ self._l7_data[u"server"][u"tcp"] = dict()
+ self._l7_data[u"server"][u"tcp"][u"accepted_flows"] = None
+ self._l7_data[u"server"][u"tcp"][u"connects"] = None
+ self._l7_data[u"server"][u"tcp"][u"closed_flows"] = None
+ else:
+ logger.warn(u"Unsupported T-Rex ASTF traffic profile!")
+ index = 0
+ for line in stdout.splitlines():
+ if f"Xstats snapshot {index}: " in line:
+ xstats.append(line[19:])
+ index += 1
+ self._xstats = tuple(xstats)
+ else:
+ self._target_duration = duration
+ self._duration = computed_duration
+ self._parse_traffic_results(stdout)
+
+ def trex_stl_start_remote_exec(self, duration, rate, async_call=False):
+ """Execute T-Rex STL script on remote node over ssh to start running
+ traffic.
+
+ In sync mode, measurement results are stored internally.
+ In async mode, initial data including xstats are stored internally.
+
+ Mode-unaware code (e.g. in search algorithms) works with transactions.
+ To keep the logic simple, multiplier is set to that value.
+ As bidirectional traffic profiles send packets in both directions,
+ they are treated as transactions with two packets (one per direction).
+
+ :param duration: Time expressed in seconds for how long to send traffic.
+ :param rate: Traffic rate in transactions per second.
+ :param async_call: If enabled then don't wait for all incoming traffic.