"""Performance testing traffic generator library."""
+import time
+
from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn
from .DropRateSearch import DropRateSearch
from .Constants import Constants
-from .ssh import SSH
+from .ssh import exec_cmd_no_error, exec_cmd
from .topology import NodeType
from .topology import NodeSubTypeTG
from .topology import Topology
__all__ = ['TGDropRateSearchImpl', 'TrafficGenerator', 'OptimizedSearch']
+def check_subtype(node):
+ """Return supported subtype of given node, or raise an exception.
+
+ Currently only one subtype is supported,
+ but we want our code to be ready for other ones.
+
+ :param node: Topology node to check. Can be None.
+ :type node: dict or NoneType
+ :returns: Subtype detected.
+ :rtype: NodeSubTypeTG
+ :raises RuntimeError: If node is not supported, message explains how.
+ """
+ if node.get('type') is None:
+ raise RuntimeError('Node type is not defined')
+ elif node['type'] != NodeType.TG:
+ raise RuntimeError('Node type is {typ!r}, not a TG'.format(
+ typ=node['type']))
+ elif node.get('subtype') is None:
+ raise RuntimeError('TG subtype is not defined')
+ elif node['subtype'] == NodeSubTypeTG.TREX:
+ return NodeSubTypeTG.TREX
+ raise RuntimeError('TG subtype {sub!r} is not supported'.format(
+ sub=node['subtype']))
+
+
class TGDropRateSearchImpl(DropRateSearch):
"""Drop Rate Search implementation."""
# to be able to use trex_stl-*()
tg_instance = BuiltIn().get_library_instance(
'resources.libraries.python.TrafficGenerator')
-
- if tg_instance.node['subtype'] is None:
- raise RuntimeError('TG subtype not defined')
- elif tg_instance.node['subtype'] == NodeSubTypeTG.TREX:
+ subtype = check_subtype(tg_instance.node)
+ if subtype == NodeSubTypeTG.TREX:
unit_rate = str(rate) + self.get_rate_type_str()
if skip_warmup:
- tg_instance.trex_stl_start_remote_exec(self.get_duration(),
- unit_rate, frame_size,
- traffic_profile,
- warmup_time=0.0)
+ tg_instance.trex_stl_start_remote_exec(
+ self.get_duration(), unit_rate, frame_size, traffic_profile,
+ warmup_time=0.0)
else:
- tg_instance.trex_stl_start_remote_exec(self.get_duration(),
- unit_rate, frame_size,
- traffic_profile)
+ tg_instance.trex_stl_start_remote_exec(
+ self.get_duration(), unit_rate, frame_size, traffic_profile)
loss = tg_instance.get_loss()
sent = tg_instance.get_sent()
if self.loss_acceptance_type_is_percentage():
loss = (float(loss) / float(sent)) * 100
-
- logger.trace("comparing: {} < {} {}".format(loss,
- loss_acceptance,
- loss_acceptance_type))
- if float(loss) > float(loss_acceptance):
- return False
- else:
- return True
- else:
- raise NotImplementedError("TG subtype not supported")
+ logger.trace("comparing: {los} < {acc} {typ}".format(
+ los=loss, acc=loss_acceptance, typ=loss_acceptance_type))
+ return float(loss) <= float(loss_acceptance)
def get_latency(self):
"""Returns min/avg/max latency.
ROBOT_LIBRARY_SCOPE = 'TEST SUITE'
def __init__(self):
+ # TODO: Number of fields will be reduced with CSIT-1378.
+ self._node = None
+ # T-REX interface order mapping
+ self._ifaces_reordered = False
+ # Result holding fields, to be removed.
self._result = None
self._loss = None
self._sent = None
self._latency = None
self._received = None
- self._node = None
- # T-REX interface order mapping
- self._ifaces_reordered = False
- # Parameters not given by measure().
+ # Measurement input fields, needed for async stop result.
+ self._start_time = None
+ self._rate = None
+ # Other input parameters, not knowable from measure() signature.
self.frame_size = None
self.traffic_profile = None
self.warmup_time = None
+ self.traffic_directions = None
@property
def node(self):
:returns: nothing
:raises RuntimeError: In case of issue during initialization.
"""
- if tg_node['type'] != NodeType.TG:
- raise RuntimeError('Node type is not a TG')
- self._node = tg_node
-
- if tg_node['subtype'] == NodeSubTypeTG.TREX:
- ssh = SSH()
- ssh.connect(tg_node)
-
- (ret, _, _) = ssh.exec_command(
- "sudo -E sh -c '{0}/resources/tools/trex/"
- "trex_installer.sh {1}'".format(Constants.REMOTE_FW_DIR,
- Constants.TREX_INSTALL_VERSION),
- timeout=1800)
- if int(ret) != 0:
- raise RuntimeError('TRex installation failed.')
-
- if1_pci = Topology().get_interface_pci_addr(tg_node, tg_if1)
- if2_pci = Topology().get_interface_pci_addr(tg_node, tg_if2)
- if1_addr = Topology().get_interface_mac(tg_node, tg_if1)
- if2_addr = Topology().get_interface_mac(tg_node, tg_if2)
+ subtype = check_subtype(tg_node)
+ if subtype == NodeSubTypeTG.TREX:
+ self._node = tg_node
+
+ if1_pci = Topology().get_interface_pci_addr(self._node, tg_if1)
+ if2_pci = Topology().get_interface_pci_addr(self._node, tg_if2)
+ if1_addr = Topology().get_interface_mac(self._node, tg_if1)
+ if2_addr = Topology().get_interface_mac(self._node, tg_if2)
if osi_layer == 'L2':
if1_adj_addr = if2_addr
if2_adj_addr = Topology().get_interface_mac(tg_if2_adj_node,
tg_if2_adj_if)
elif osi_layer == 'L7':
- if1_addr = Topology().get_interface_ip4(tg_node, tg_if1)
- if2_addr = Topology().get_interface_ip4(tg_node, tg_if2)
+ if1_addr = Topology().get_interface_ip4(self._node, tg_if1)
+ if2_addr = Topology().get_interface_ip4(self._node, tg_if2)
if1_adj_addr = Topology().get_interface_ip4(tg_if1_adj_node,
tg_if1_adj_if)
if2_adj_addr = Topology().get_interface_ip4(tg_if2_adj_node,
self._ifaces_reordered = True
if osi_layer == 'L2' or osi_layer == 'L3':
- (ret, _, _) = ssh.exec_command(
- "sudo sh -c 'cat << EOF > /etc/trex_cfg.yaml\n"
- "- port_limit: 2\n"
- " version: 2\n"
+ exec_cmd_no_error(
+ self._node,
+ "sh -c 'cat << EOF > /etc/trex_cfg.yaml\n"
+ "- version: 2\n"
" interfaces: [\"{0}\",\"{1}\"]\n"
" port_info:\n"
" - dest_mac: [{2}]\n"
"0x"+if1_adj_addr.replace(":", ",0x"),
"0x"+if1_addr.replace(":", ",0x"),
"0x"+if2_adj_addr.replace(":", ",0x"),
- "0x"+if2_addr.replace(":", ",0x")))
+ "0x"+if2_addr.replace(":", ",0x")),
+ sudo=True, message='TRex config generation error')
elif osi_layer == 'L7':
- (ret, _, _) = ssh.exec_command(
- "sudo sh -c 'cat << EOF > /etc/trex_cfg.yaml\n"
- "- port_limit: 2\n"
- " version: 2\n"
+ exec_cmd_no_error(
+ self._node,
+ "sh -c 'cat << EOF > /etc/trex_cfg.yaml\n"
+ "- version: 2\n"
" interfaces: [\"{0}\",\"{1}\"]\n"
" port_info:\n"
" - ip: [{2}]\n"
"EOF'"\
.format(if1_pci, if2_pci,
if1_addr, if1_adj_addr,
- if2_addr, if2_adj_addr))
+ if2_addr, if2_adj_addr),
+ sudo=True, message='TRex config generation error')
else:
raise ValueError("Unknown Test Type")
- if int(ret) != 0:
- raise RuntimeError('TRex config generation error')
-
- for _ in range(0, 3):
- # kill TRex only if it is already running
- ssh.exec_command(
- "sh -c 'pgrep t-rex && sudo pkill t-rex && sleep 3'")
-
- # configure TRex
- (ret, _, _) = ssh.exec_command(
- "sh -c 'cd {0}/scripts/ && sudo ./trex-cfg'"\
- .format(Constants.TREX_INSTALL_DIR))
- if int(ret) != 0:
- raise RuntimeError('trex-cfg failed')
-
- # start TRex
- if osi_layer == 'L2' or osi_layer == 'L3':
- (ret, _, _) = ssh.exec_command(
- "sh -c 'cd {0}/scripts/ && "
- "sudo nohup ./t-rex-64 -i -c 7 --iom 0 > /tmp/trex.log "
- "2>&1 &' > /dev/null"\
- .format(Constants.TREX_INSTALL_DIR))
- elif osi_layer == 'L7':
- (ret, _, _) = ssh.exec_command(
- "sh -c 'cd {0}/scripts/ && "
- "sudo nohup ./t-rex-64 --astf -i -c 7 --iom 0 > "
- "/tmp/trex.log 2>&1 &' > /dev/null"\
- .format(Constants.TREX_INSTALL_DIR))
- else:
- raise ValueError("Unknown Test Type")
- if int(ret) != 0:
- ssh.exec_command("sh -c 'cat /tmp/trex.log'")
- raise RuntimeError('t-rex-64 startup failed')
-
- # get TRex server info
- (ret, _, _) = ssh.exec_command(
- "sh -c 'sleep 3; "
- "{0}/resources/tools/trex/trex_server_info.py'"\
- .format(Constants.REMOTE_FW_DIR),
- timeout=120)
- if int(ret) == 0:
- # If we get info TRex is running
- return
- # after max retries TRex is still not responding to API
- # critical error occurred
- raise RuntimeError('t-rex-64 startup failed')
+
+ self._startup_trex(osi_layer)
+
+ def _startup_trex(self, osi_layer):
+ """Startup sequence for the TRex traffic generator.
+
+ :param osi_layer: 'L2', 'L3' or 'L7' - OSI Layer testing type.
+ :type osi_layer: str
+ :raises RuntimeError: If node subtype is not a TREX or startup failed.
+ """
+ # No need to check subtype, we know it is TREX.
+ for _ in range(0, 3):
+ # Kill TRex only if it is already running.
+ cmd = "sh -c 'pgrep t-rex && pkill t-rex && sleep 3 || true'"
+ exec_cmd_no_error(
+ self._node, cmd, sudo=True, message='Kill TRex failed!')
+
+ # Configure TRex.
+ ports = ''
+ for port in self._node['interfaces'].values():
+ ports += ' {pci}'.format(pci=port.get('pci_address'))
+
+ cmd = ("sh -c 'cd {dir}/scripts/ && "
+ "./dpdk_nic_bind.py -u {ports} || true'"
+ .format(dir=Constants.TREX_INSTALL_DIR, ports=ports))
+ exec_cmd_no_error(
+ self._node, cmd, sudo=True,
+ message='Unbind PCI ports from driver failed!')
+
+ cmd = ("sh -c 'cd {dir}/scripts/ && ./trex-cfg "
+ "--unbind-unused-ports'"
+ .format(dir=Constants.TREX_INSTALL_DIR))
+ exec_cmd_no_error(
+ self._node, cmd, sudo=True, message='Config TRex failed!')
+
+ # Start TRex.
+ cmd = ("sh -c 'cd {dir}/scripts/ && "
+ "nohup ./t-rex-64 {mode} -i -c 7 > "
+ "/tmp/trex.log 2>&1 &' > /dev/null"
+ .format(dir=Constants.TREX_INSTALL_DIR,
+ mode='--astf' if osi_layer == 'L7' else ''))
+ try:
+ exec_cmd_no_error(self._node, cmd, sudo=True)
+ except RuntimeError:
+ cmd = "sh -c 'cat /tmp/trex.log'"
+ exec_cmd_no_error(
+ self._node, cmd, sudo=True, message='Get TRex logs failed!')
+ raise RuntimeError('Start TRex failed!')
+
+ # Test if TRex starts successfuly.
+ cmd = ("sh -c '{dir}/resources/tools/trex/trex_server_info.py'"
+ .format(dir=Constants.REMOTE_FW_DIR))
+ try:
+ exec_cmd_no_error(
+ self._node, cmd, sudo=True, message='Test TRex failed!',
+ retries=20)
+ except RuntimeError:
+ continue
+ return
+ # After max retries TRex is still not responding to API critical error
+ # occurred.
+ raise RuntimeError('Start TRex failed after multiple retries!')
@staticmethod
def is_trex_running(node):
:rtype: bool
:raises RuntimeError: If node type is not a TG.
"""
- if node['type'] != NodeType.TG:
- raise RuntimeError('Node type is not a TG')
+ # No need to check subtype, we know it is TREX.
- ssh = SSH()
- ssh.connect(node)
- ret, _, _ = ssh.exec_command_sudo("pidof t-rex")
+ ret, _, _ = exec_cmd(node, "pidof t-rex", sudo=True)
return bool(int(ret) == 0)
@staticmethod
:raises RuntimeError: If node type is not a TG,
or if TRex teardown fails.
"""
- if node['type'] != NodeType.TG:
- raise RuntimeError('Node type is not a TG')
- if node['subtype'] == NodeSubTypeTG.TREX:
- ssh = SSH()
- ssh.connect(node)
- (ret, _, _) = ssh.exec_command(
- "sh -c 'sudo pkill t-rex && sleep 3'")
- if int(ret) != 0:
- raise RuntimeError('pkill t-rex failed')
+ subtype = check_subtype(node)
+ if subtype == NodeSubTypeTG.TREX:
+ exec_cmd_no_error(
+ node, "sh -c 'sudo pkill t-rex && sleep 3'",
+ sudo=False, message='pkill t-rex failed')
- @staticmethod
- def trex_stl_stop_remote_exec(node):
+ def _parse_traffic_results(self, stdout):
+ """Parse stdout of scripts into fieds of self.
+
+ Block of code to reuse, by sync start, or stop after async.
+ TODO: Is the output TG subtype dependent?
+
+ :param stdout: Text containing the standard output.
+ :type stdout: str
+ """
+ # last line from console output
+ line = stdout.splitlines()[-1]
+ self._result = line
+ logger.info('TrafficGen result: {0}'.format(self._result))
+ self._received = self._result.split(', ')[1].split('=')[1]
+ self._sent = self._result.split(', ')[2].split('=')[1]
+ self._loss = self._result.split(', ')[3].split('=')[1]
+ self._latency = []
+ self._latency.append(self._result.split(', ')[4].split('=')[1])
+ self._latency.append(self._result.split(', ')[5].split('=')[1])
+
+ 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.
+
:param node: TRex generator node.
:type node: dict
:returns: Nothing
:raises RuntimeError: If stop traffic script fails.
"""
- ssh = SSH()
- ssh.connect(node)
-
- (ret, _, _) = ssh.exec_command(
+ # No need to check subtype, we know it is TREX.
+ stdout, _ = exec_cmd_no_error(
+ node,
"sh -c '{}/resources/tools/trex/"
- "trex_stateless_stop.py'".format(Constants.REMOTE_FW_DIR))
-
- if int(ret) != 0:
- raise RuntimeError('TRex stateless runtime error')
+ "trex_stateless_stop.py'".format(Constants.REMOTE_FW_DIR),
+ 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.
: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.
"""
- ssh = SSH()
- ssh.connect(self._node)
+ # No need to check subtype, we know it is TREX.
reorder = self._ifaces_reordered # Just to make the next line fit.
p_0, p_1 = (rx_port, tx_port) if reorder else (tx_port, rx_port)
+ # Values from Robot can introduce type unicode,
+ # we need to encode them, so that repr() does not lead with 'u'.
+ if isinstance(rate, unicode):
+ rate = rate.encode("utf-8")
+ if not isinstance(duration, (float, int)):
+ duration = float(duration)
+ if not isinstance(warmup_time, (float, int)):
+ warmup_time = float(warmup_time)
command = (
"sh -c '{tool}/resources/tools/trex/trex_stateless_profile.py"
" --profile {prof}/resources/traffic_profiles/trex/{traffic}.py"
- " --duration {duration} --frame_size {frame_size} --rate {rate}"
- " --warmup_time {warmup} --port_0 {p_0} --port_1 {p_1}").format(
+ " --duration {duration!r} --frame_size {frame_size} --rate {rate!r}"
+ " --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 += "'"
- (ret, stdout, _) = ssh.exec_command(
- command, timeout=float(duration) + 60)
+ stdout, _ = exec_cmd_no_error(
+ self._node, command, timeout=float(duration) + 60,
+ message='TRex stateless runtime error')
- if int(ret) != 0:
- raise RuntimeError('TRex stateless runtime error')
- elif async_call:
+ if async_call:
#no result
+ self._start_time = time.time()
+ self._rate = float(rate[:-3]) if "pps" in rate else rate
self._received = None
self._sent = None
self._loss = None
self._latency = None
else:
- # last line from console output
- line = stdout.splitlines()[-1]
- self._result = line
- logger.info('TrafficGen result: {0}'.format(self._result))
- self._received = self._result.split(', ')[1].split('=')[1]
- self._sent = self._result.split(', ')[2].split('=')[1]
- self._loss = self._result.split(', ')[3].split('=')[1]
- self._latency = []
- self._latency.append(self._result.split(', ')[4].split('=')[1])
- self._latency.append(self._result.split(', ')[5].split('=')[1])
+ self._parse_traffic_results(stdout)
+ self._start_time = None
+ self._rate = None
def stop_traffic_on_tg(self):
"""Stop all traffic on TG.
:returns: Nothing
:raises RuntimeError: If TG is not set.
"""
- if self._node is None:
- raise RuntimeError("TG is not set")
- if self._node['subtype'] == NodeSubTypeTG.TREX:
+ subtype = check_subtype(self._node)
+ if subtype == NodeSubTypeTG.TREX:
self.trex_stl_stop_remote_exec(self._node)
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.
: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.
or if subtype is not specified.
:raises NotImplementedError: If TG is not supported.
"""
-
- node = self._node
- if node is None:
- raise RuntimeError("TG is not set")
-
- if node['type'] != NodeType.TG:
- raise RuntimeError('Node type is not a TG')
-
- if node['subtype'] is None:
- raise RuntimeError('TG subtype not defined')
- elif node['subtype'] == NodeSubTypeTG.TREX:
+ subtype = check_subtype(self._node)
+ 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)
- else:
- raise NotImplementedError("TG subtype not supported")
+ 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.
+
+ Separate function, as measurements can end either by time
+ or by explicit call, this is the common block at the end.
+
+ TODO: Fail on running or already reported measurement.
+
+ :param duration: Measurement duration [s] if known beforehand.
+ For explicitly stopped measurement it is estimated.
+ :param transmit_rate: Target aggregate transmit rate [pps].
+ If not given, computed assuming it was bidirectional.
+ :type duration: float or NoneType
+ :type transmit_rate: float or NoneType
+ :returns: Structure containing the result of the measurement.
+ :rtype: ReceiveRateMeasurement
+ """
+ if duration is None:
+ duration = time.time() - self._start_time
+ self._start_time = None
+ if transmit_rate is None:
+ transmit_rate = self._rate * self.traffic_directions
+ transmit_count = int(self.get_sent())
+ loss_count = int(self.get_loss())
+ measurement = ReceiveRateMeasurement(
+ duration, transmit_rate, transmit_count, loss_count)
+ measurement.latency = self.get_latency_int()
+ 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)
- # Trex needs target Tr per stream, but reports aggregate Tx and Dx.
- unit_rate = str(transmit_rate / 2.0) + "pps"
+ # TG needs target Tr per stream, but reports aggregate Tx and Dx.
+ 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)
- transmit_count = int(self.get_sent())
- loss_count = int(self.get_loss())
- measurement = ReceiveRateMeasurement(
- duration, transmit_rate, transmit_count, loss_count)
- measurement.latency = self.get_latency_int()
- return measurement
+ 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,
@staticmethod
def perform_soak_search(
frame_size, traffic_profile, minimum_transmit_rate,
- maximum_transmit_rate, plr_target=1e-7, tdpt=0.2,
- initial_count=50, timeout=1800.0):
+ maximum_transmit_rate, plr_target=1e-7, tdpt=0.1,
+ 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,
- trial_number_offset=initial_count, timeout=timeout)
+ trial_number_offset=initial_count, timeout=timeout,
+ trace_enabled=trace_enabled)
result = algorithm.search(minimum_transmit_rate, maximum_transmit_rate)
return result