logger.trace("comparing: {los} < {acc} {typ}".format(
los=loss, acc=loss_acceptance, typ=loss_acceptance_type))
return float(loss) <= float(loss_acceptance)
+ return False
def get_latency(self):
"""Returns min/avg/max latency.
self.frame_size = None
self.traffic_profile = None
self.warmup_time = None
+ self.traffic_directions = None
+ # Transient data needed for async measurements.
+ self._xstats = (None, None)
+ # TODO: Rename "xstats" to something opaque, so TRex is not privileged?
@property
def node(self):
# Start TRex.
cmd = ("sh -c 'cd {dir}/scripts/ && "
- "nohup ./t-rex-64 {mode} -i -c 7 > "
+ "nohup ./t-rex-64 --hdrh{mode} -i -c 7 > "
"/tmp/trex.log 2>&1 &' > /dev/null"
.format(dir=Constants.TREX_INSTALL_DIR,
- mode='--astf' if osi_layer == 'L7' else ''))
+ mode=' --astf' if osi_layer == 'L7' else ''))
try:
exec_cmd_no_error(self._node, cmd, sudo=True)
except RuntimeError:
sudo=False, message='pkill t-rex failed')
def _parse_traffic_results(self, stdout):
- """Parse stdout of scripts into fieds of self.
+ """Parse stdout of scripts into fields of self.
Block of code to reuse, by sync start, or stop after async.
TODO: Is the output TG subtype dependent?
def trex_stl_stop_remote_exec(self, node):
"""Execute script on remote node over ssh to stop running traffic.
- Internal state is updated with results.
+ Internal state is updated with measurement results.
:param node: TRex generator node.
:type node: dict
- :returns: Nothing
:raises RuntimeError: If stop traffic script fails.
"""
# No need to check subtype, we know it is TREX.
+ x_args = ""
+ for index, value in enumerate(self._xstats):
+ if value is not None:
+ # Nested quoting is fun.
+ value = value.replace("'", "\"")
+ x_args += " --xstat{i}='\"'\"'{v}'\"'\"'".format(
+ i=index, v=value)
stdout, _ = exec_cmd_no_error(
- node,
- "sh -c '{}/resources/tools/trex/"
- "trex_stateless_stop.py'".format(Constants.REMOTE_FW_DIR),
+ node, "sh -c '{d}/resources/tools/trex/trex_stateless_stop.py{a}'"\
+ .format(d=Constants.REMOTE_FW_DIR, a=x_args),
message='TRex stateless runtime error')
self._parse_traffic_results(stdout)
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.
+ In sync mode, measurement results are stored internally.
+ In async mode, initial data including xstats are stored internally.
+
:param duration: Time expresed in seconds for how long to send traffic.
:param rate: Traffic rate expressed with units (pps, %)
:param frame_size: L2 frame size to send (without padding and IPG).
: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.
: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.
"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"
+ command += " --async_start"
if latency:
command += " --latency"
- if unidirection:
- command += " --unidirection"
command += "'"
stdout, _ = exec_cmd_no_error(
self._node, command, timeout=float(duration) + 60,
message='TRex stateless runtime error')
+ self.traffic_directions = traffic_directions
if async_call:
#no result
self._start_time = time.time()
- self._rate = float(rate[:-3]) if "pps" in rate else rate
+ self._rate = float(rate[:-3]) if "pps" in rate else float(rate)
self._received = None
self._sent = None
self._loss = None
self._latency = None
+ xstats = [None, None]
+ index = 0
+ for line in stdout.splitlines():
+ if "Xstats snapshot {i}: ".format(i=index) in line:
+ xstats[index] = line[19:]
+ index += 1
+ if index == 2:
+ break
+ self._xstats = tuple(xstats)
else:
self._parse_traffic_results(stdout)
self._start_time = None
def stop_traffic_on_tg(self):
"""Stop all traffic on TG.
- :returns: Nothing
+ :returns: Structure containing the result of the measurement.
+ :rtype: ReceiveRateMeasurement
:raises RuntimeError: If TG is not set.
"""
subtype = check_subtype(self._node)
if subtype == NodeSubTypeTG.TREX:
self.trex_stl_stop_remote_exec(self._node)
+ return self.get_measurement_result()
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.
+ In async mode, xstats is stored internally,
+ to enable getting correct result when stopping the traffic.
+ In both modes, stdout is returned,
+ but _parse_traffic_results only works in sync output.
+
Note that bidirectional traffic also contains flows
transmitted from rx_port and received in tx_port.
But some tests use asymmetric traffic, so those arguments are relevant.
: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.
: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.
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
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.
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 * self.traffic_directions
transmit_count = int(self.get_sent())
loss_count = int(self.get_loss())
measurement = ReceiveRateMeasurement(
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.
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(
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
: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
: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,
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.
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
: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,