1 # Copyright (c) 2018 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """Performance testing traffic generator library."""
16 from robot.api import logger
17 from robot.libraries.BuiltIn import BuiltIn
19 from .DropRateSearch import DropRateSearch
20 from .constants import Constants
22 from .topology import NodeType
23 from .topology import NodeSubTypeTG
24 from .topology import Topology
25 from .MLRsearch.AbstractMeasurer import AbstractMeasurer
26 from .MLRsearch.MultipleLossRatioSearch import MultipleLossRatioSearch
27 from .MLRsearch.ReceiveRateMeasurement import ReceiveRateMeasurement
28 from .PLRsearch.PLRsearch import PLRsearch
30 __all__ = ['TGDropRateSearchImpl', 'TrafficGenerator', 'OptimizedSearch']
33 class TGDropRateSearchImpl(DropRateSearch):
34 """Drop Rate Search implementation."""
37 super(TGDropRateSearchImpl, self).__init__()
39 def measure_loss(self, rate, frame_size, loss_acceptance,
40 loss_acceptance_type, traffic_type, skip_warmup=False):
41 """Runs the traffic and evaluate the measured results.
43 :param rate: Offered traffic load.
44 :param frame_size: Size of frame.
45 :param loss_acceptance: Permitted drop ratio or frames count.
46 :param loss_acceptance_type: Type of permitted loss.
47 :param traffic_type: Module name as a traffic type identifier.
48 See resources/traffic_profiles/trex for implemented modules.
49 :param skip_warmup: Start TRex without warmup traffic if true.
52 :type loss_acceptance: float
53 :type loss_acceptance_type: LossAcceptanceType
54 :type traffic_type: str
55 :type skip_warmup: bool
56 :returns: Drop threshold exceeded? (True/False)
58 :raises NotImplementedError: If TG is not supported.
59 :raises RuntimeError: If TG is not specified.
61 # we need instance of TrafficGenerator instantiated by Robot Framework
62 # to be able to use trex_stl-*()
63 tg_instance = BuiltIn().get_library_instance(
64 'resources.libraries.python.TrafficGenerator')
66 if tg_instance.node['subtype'] is None:
67 raise RuntimeError('TG subtype not defined')
68 elif tg_instance.node['subtype'] == NodeSubTypeTG.TREX:
69 unit_rate = str(rate) + self.get_rate_type_str()
71 tg_instance.trex_stl_start_remote_exec(self.get_duration(),
72 unit_rate, frame_size,
76 tg_instance.trex_stl_start_remote_exec(self.get_duration(),
77 unit_rate, frame_size,
79 loss = tg_instance.get_loss()
80 sent = tg_instance.get_sent()
81 if self.loss_acceptance_type_is_percentage():
82 loss = (float(loss) / float(sent)) * 100
84 logger.trace("comparing: {} < {} {}".format(loss,
86 loss_acceptance_type))
87 if float(loss) > float(loss_acceptance):
92 raise NotImplementedError("TG subtype not supported")
94 def get_latency(self):
95 """Returns min/avg/max latency.
97 :returns: Latency stats.
100 tg_instance = BuiltIn().get_library_instance(
101 'resources.libraries.python.TrafficGenerator')
102 return tg_instance.get_latency_int()
105 class TrafficGenerator(AbstractMeasurer):
106 """Traffic Generator.
108 FIXME: Describe API."""
110 # TODO: Decrease friction between various search and rate provider APIs.
111 # TODO: Remove "trex" from lines which could work with other TGs.
113 # Use one instance of TrafficGenerator for all tests in test suite
114 ROBOT_LIBRARY_SCOPE = 'TEST SUITE'
121 self._received = None
123 # T-REX interface order mapping
124 self._ifaces_reordered = False
125 # Parameters not given by measure().
126 self.frame_size = None
127 self.traffic_type = None
128 self.warmup_time = None
134 :returns: Traffic generator node.
140 """Return number of lost packets.
142 :returns: Number of lost packets.
148 """Return number of sent packets.
150 :returns: Number of sent packets.
155 def get_received(self):
156 """Return number of received packets.
158 :returns: Number of received packets.
161 return self._received
163 def get_latency_int(self):
164 """Return rounded min/avg/max latency.
166 :returns: Latency stats.
171 def initialize_traffic_generator(self, tg_node, tg_if1, tg_if2,
172 tg_if1_adj_node, tg_if1_adj_if,
173 tg_if2_adj_node, tg_if2_adj_if,
175 tg_if1_dst_mac=None, tg_if2_dst_mac=None):
176 """TG initialization.
178 :param tg_node: Traffic generator node.
179 :param tg_if1: TG - name of first interface.
180 :param tg_if2: TG - name of second interface.
181 :param tg_if1_adj_node: TG if1 adjecent node.
182 :param tg_if1_adj_if: TG if1 adjecent interface.
183 :param tg_if2_adj_node: TG if2 adjecent node.
184 :param tg_if2_adj_if: TG if2 adjecent interface.
185 :param test_type: 'L2', 'L3' or 'L7' - OSI Layer testing type.
186 :param tg_if1_dst_mac: Interface 1 destination MAC address.
187 :param tg_if2_dst_mac: Interface 2 destination MAC address.
191 :type tg_if1_adj_node: dict
192 :type tg_if1_adj_if: str
193 :type tg_if2_adj_node: dict
194 :type tg_if2_adj_if: str
196 :type tg_if1_dst_mac: str
197 :type tg_if2_dst_mac: str
199 :raises RuntimeError: In case of issue during initialization.
201 if tg_node['type'] != NodeType.TG:
202 raise RuntimeError('Node type is not a TG')
205 if tg_node['subtype'] == NodeSubTypeTG.TREX:
209 (ret, _, _) = ssh.exec_command(
210 "sudo -E sh -c '{0}/resources/tools/trex/"
211 "trex_installer.sh {1}'".format(Constants.REMOTE_FW_DIR,
212 Constants.TREX_INSTALL_VERSION),
215 raise RuntimeError('TRex installation failed.')
217 if1_pci = Topology().get_interface_pci_addr(tg_node, tg_if1)
218 if2_pci = Topology().get_interface_pci_addr(tg_node, tg_if2)
219 if1_addr = Topology().get_interface_mac(tg_node, tg_if1)
220 if2_addr = Topology().get_interface_mac(tg_node, tg_if2)
222 if test_type == 'L2':
223 if1_adj_addr = if2_addr
224 if2_adj_addr = if1_addr
225 elif test_type == 'L3':
226 if1_adj_addr = Topology().get_interface_mac(tg_if1_adj_node,
228 if2_adj_addr = Topology().get_interface_mac(tg_if2_adj_node,
230 elif test_type == 'L7':
231 if1_addr = Topology().get_interface_ip4(tg_node, tg_if1)
232 if2_addr = Topology().get_interface_ip4(tg_node, tg_if2)
233 if1_adj_addr = Topology().get_interface_ip4(tg_if1_adj_node,
235 if2_adj_addr = Topology().get_interface_ip4(tg_if2_adj_node,
238 raise ValueError("Unknown Test Type")
240 # in case of switched environment we can override MAC addresses
241 if tg_if1_dst_mac is not None and tg_if2_dst_mac is not None:
242 if1_adj_addr = tg_if1_dst_mac
243 if2_adj_addr = tg_if2_dst_mac
245 if min(if1_pci, if2_pci) != if1_pci:
246 if1_pci, if2_pci = if2_pci, if1_pci
247 if1_addr, if2_addr = if2_addr, if1_addr
248 if1_adj_addr, if2_adj_addr = if2_adj_addr, if1_adj_addr
249 self._ifaces_reordered = True
251 if test_type == 'L2' or test_type == 'L3':
252 (ret, _, _) = ssh.exec_command(
253 "sudo sh -c 'cat << EOF > /etc/trex_cfg.yaml\n"
256 " interfaces: [\"{0}\",\"{1}\"]\n"
258 " - dest_mac: [{2}]\n"
260 " - dest_mac: [{4}]\n"
263 .format(if1_pci, if2_pci,
264 "0x"+if1_adj_addr.replace(":", ",0x"),
265 "0x"+if1_addr.replace(":", ",0x"),
266 "0x"+if2_adj_addr.replace(":", ",0x"),
267 "0x"+if2_addr.replace(":", ",0x")))
268 elif test_type == 'L7':
269 (ret, _, _) = ssh.exec_command(
270 "sudo sh -c 'cat << EOF > /etc/trex_cfg.yaml\n"
273 " interfaces: [\"{0}\",\"{1}\"]\n"
276 " default_gw: [{3}]\n"
278 " default_gw: [{5}]\n"
280 .format(if1_pci, if2_pci,
281 if1_addr, if1_adj_addr,
282 if2_addr, if2_adj_addr))
284 raise ValueError("Unknown Test Type")
286 raise RuntimeError('TRex config generation error')
288 for _ in range(0, 3):
289 # kill TRex only if it is already running
291 "sh -c 'pgrep t-rex && sudo pkill t-rex && sleep 3'")
294 (ret, _, _) = ssh.exec_command(
295 "sh -c 'cd {0}/scripts/ && sudo ./trex-cfg'"\
296 .format(Constants.TREX_INSTALL_DIR))
298 raise RuntimeError('trex-cfg failed')
301 if test_type == 'L2' or test_type == 'L3':
302 (ret, _, _) = ssh.exec_command(
303 "sh -c 'cd {0}/scripts/ && "
304 "sudo nohup ./t-rex-64 -i -c 7 --iom 0 > /tmp/trex.log "
305 "2>&1 &' > /dev/null"\
306 .format(Constants.TREX_INSTALL_DIR))
307 elif test_type == 'L7':
308 (ret, _, _) = ssh.exec_command(
309 "sh -c 'cd {0}/scripts/ && "
310 "sudo nohup ./t-rex-64 --astf -i -c 7 --iom 0 > "
311 "/tmp/trex.log 2>&1 &' > /dev/null"\
312 .format(Constants.TREX_INSTALL_DIR))
314 raise ValueError("Unknown Test Type")
316 ssh.exec_command("sh -c 'cat /tmp/trex.log'")
317 raise RuntimeError('t-rex-64 startup failed')
319 # get TRex server info
320 (ret, _, _) = ssh.exec_command(
322 "{0}/resources/tools/trex/trex_server_info.py'"\
323 .format(Constants.REMOTE_FW_DIR),
326 # If we get info TRex is running
328 # after max retries TRex is still not responding to API
329 # critical error occurred
330 raise RuntimeError('t-rex-64 startup failed')
333 def is_trex_running(node):
334 """Check if TRex is running using pidof.
336 :param node: Traffic generator node.
338 :returns: True if TRex is running otherwise False.
340 :raises RuntimeError: If node type is not a TG.
342 if node['type'] != NodeType.TG:
343 raise RuntimeError('Node type is not a TG')
347 ret, _, _ = ssh.exec_command_sudo("pidof t-rex")
348 return bool(int(ret) == 0)
351 def teardown_traffic_generator(node):
354 :param node: Traffic generator node.
357 :raises RuntimeError: If node type is not a TG,
358 or if TRex teardown fails.
360 if node['type'] != NodeType.TG:
361 raise RuntimeError('Node type is not a TG')
362 if node['subtype'] == NodeSubTypeTG.TREX:
365 (ret, _, _) = ssh.exec_command(
366 "sh -c 'sudo pkill t-rex && sleep 3'")
368 raise RuntimeError('pkill t-rex failed')
371 def trex_stl_stop_remote_exec(node):
372 """Execute script on remote node over ssh to stop running traffic.
374 :param node: TRex generator node.
377 :raises RuntimeError: If stop traffic script fails.
382 (ret, _, _) = ssh.exec_command(
383 "sh -c '{}/resources/tools/trex/"
384 "trex_stateless_stop.py'".format(Constants.REMOTE_FW_DIR))
387 raise RuntimeError('TRex stateless runtime error')
389 def trex_stl_start_remote_exec(self, duration, rate, framesize,
390 traffic_type, async_call=False,
391 latency=True, warmup_time=5.0):
392 """Execute script on remote node over ssh to start traffic.
394 :param duration: Time expresed in seconds for how long to send traffic.
395 :param rate: Traffic rate expressed with units (pps, %)
396 :param framesize: L2 frame size to send (without padding and IPG).
397 :param traffic_type: Module name as a traffic type identifier.
398 See resources/traffic_profiles/trex for implemented modules.
399 :param async_call: If enabled then don't wait for all incomming trafic.
400 :param latency: With latency measurement.
401 :param warmup_time: Warmup time period.
402 :type duration: float
405 :type traffic_type: str
406 :type async_call: bool
408 :type warmup_time: float
410 :raises RuntimeError: In case of TG driver issue.
413 ssh.connect(self._node)
415 _async = "--async" if async_call else ""
416 _latency = "--latency" if latency else ""
417 _p0, _p1 = (2, 1) if self._ifaces_reordered else (1, 2)
419 profile_path = ("{0}/resources/traffic_profiles/trex/"
420 "{1}.py".format(Constants.REMOTE_FW_DIR,
422 (ret, stdout, _) = ssh.exec_command(
424 "'{0}/resources/tools/trex/trex_stateless_profile.py "
434 format(Constants.REMOTE_FW_DIR, profile_path, duration, framesize,
435 rate, warmup_time, _p0 - 1, _p1 - 1, _async, _latency),
436 timeout=float(duration) + 60)
439 raise RuntimeError('TRex stateless runtime error')
442 self._received = None
447 # last line from console output
448 line = stdout.splitlines()[-1]
451 logger.info('TrafficGen result: {0}'.format(self._result))
453 self._received = self._result.split(', ')[1].split('=')[1]
454 self._sent = self._result.split(', ')[2].split('=')[1]
455 self._loss = self._result.split(', ')[3].split('=')[1]
458 self._latency.append(self._result.split(', ')[4].split('=')[1])
459 self._latency.append(self._result.split(', ')[5].split('=')[1])
461 def trex_stl_start_unidirection(self, duration, rate, framesize,
462 traffic_type, tx_port=0, rx_port=1,
463 async_call=False, latency=False,
465 """Execute script on remote node over ssh to start unidirection traffic.
466 The purpose of this function is to support performance test that need to
467 measure unidirectional traffic, e.g. Load balancer maglev mode and l3dsr
470 :param duration: Time expresed in seconds for how long to send traffic.
471 :param rate: Traffic rate expressed with units (pps, %)
472 :param framesize: L2 frame size to send (without padding and IPG).
473 :param traffic_type: Module name as a traffic type identifier.
474 See resources/traffic_profiles/trex for implemented modules.
475 :param tx_port: Traffic generator transmit port.
476 :param rx_port: Traffic generator receive port.
477 :param latency: With latency measurement.
478 :param async_call: If enabled then don't wait for all incomming trafic.
479 :param warmup_time: Warmup time period.
480 :type duration: float
483 :type traffic_type: str
484 :type tx_port: integer
485 :type rx_port: integer
487 :type async_call: bool
488 :type warmup_time: float
490 :raises RuntimeError: In case of TG driver issue.
493 ssh.connect(self._node)
495 _latency = "--latency" if latency else ""
496 _async = "--async" if async_call else ""
498 profile_path = ("{0}/resources/traffic_profiles/trex/"
499 "{1}.py".format(Constants.REMOTE_FW_DIR,
501 (ret, stdout, _) = ssh.exec_command(
503 "'{0}/resources/tools/trex/trex_stateless_profile.py "
513 "{10}'". # --unidirection
514 format(Constants.REMOTE_FW_DIR, profile_path, duration, framesize,
515 rate, warmup_time, tx_port, rx_port, _async, _latency,
517 timeout=float(duration) + 60)
520 raise RuntimeError('TRex unidirection runtime error')
523 self._received = None
528 # last line from console output
529 line = stdout.splitlines()[-1]
532 logger.info('TrafficGen result: {0}'.format(self._result))
534 self._received = self._result.split(', ')[1].split('=')[1]
535 self._sent = self._result.split(', ')[2].split('=')[1]
536 self._loss = self._result.split(', ')[3].split('=')[1]
538 self._latency.append(self._result.split(', ')[4].split('=')[1])
540 def stop_traffic_on_tg(self):
541 """Stop all traffic on TG.
544 :raises RuntimeError: If TG is not set.
546 if self._node is None:
547 raise RuntimeError("TG is not set")
548 if self._node['subtype'] == NodeSubTypeTG.TREX:
549 self.trex_stl_stop_remote_exec(self._node)
551 def send_traffic_on_tg(self, duration, rate, framesize, traffic_type,
552 unidirection=False, tx_port=0, rx_port=1,
553 warmup_time=5, async_call=False, latency=True):
554 """Send traffic from all configured interfaces on TG.
556 :param duration: Duration of test traffic generation in seconds.
557 :param rate: Offered load per interface (e.g. 1%, 3gbps, 4mpps, ...).
558 :param framesize: Frame size (L2) in Bytes.
559 :param traffic_type: Module name as a traffic type identifier.
560 See resources/traffic_profiles/trex for implemented modules.
561 :param unidirection: Traffic is unidirectional.
562 :param tx_port: Traffic generator transmit port.
563 :param rx_port: Traffic generator receive port.
564 :param warmup_time: Warmup phase in seconds.
565 :param async_call: Async mode.
566 :param latency: With latency measurement.
570 :type traffic_type: str
571 :type unidirection: bool
572 :type tx_port: integer
573 :type rx_port: integer
574 :type warmup_time: float
575 :type async_call: bool
579 :raises RuntimeError: If TG is not set, or if node is not TG,
580 or if subtype is not specified.
581 :raises NotImplementedError: If TG is not supported.
586 raise RuntimeError("TG is not set")
588 if node['type'] != NodeType.TG:
589 raise RuntimeError('Node type is not a TG')
591 if node['subtype'] is None:
592 raise RuntimeError('TG subtype not defined')
593 elif node['subtype'] == NodeSubTypeTG.TREX:
595 self.trex_stl_start_unidirection(duration, rate, framesize,
596 traffic_type, tx_port,
597 rx_port, async_call, latency,
600 self.trex_stl_start_remote_exec(duration, rate, framesize,
601 traffic_type, async_call,
602 latency, warmup_time)
604 raise NotImplementedError("TG subtype not supported")
608 def no_traffic_loss_occurred(self):
609 """Fail if loss occurred in traffic run.
612 :raises Exception: If loss occured.
614 if self._loss is None:
615 raise RuntimeError('The traffic generation has not been issued')
616 if self._loss != '0':
617 raise RuntimeError('Traffic loss occurred: {0}'.format(self._loss))
619 def fail_if_no_traffic_forwarded(self):
620 """Fail if no traffic forwarded.
623 :raises Exception: If no traffic forwarded.
625 if self._received is None:
626 raise RuntimeError('The traffic generation has not been issued')
627 if self._received == '0':
628 raise RuntimeError('No traffic forwarded')
630 def partial_traffic_loss_accepted(self, loss_acceptance,
631 loss_acceptance_type):
632 """Fail if loss is higher then accepted in traffic run.
634 :param loss_acceptance: Permitted drop ratio or frames count.
635 :param loss_acceptance_type: Type of permitted loss.
636 :type loss_acceptance: float
637 :type loss_acceptance_type: LossAcceptanceType
639 :raises Exception: If loss is above acceptance criteria.
641 if self._loss is None:
642 raise Exception('The traffic generation has not been issued')
644 if loss_acceptance_type == 'percentage':
645 loss = (float(self._loss) / float(self._sent)) * 100
646 elif loss_acceptance_type == 'frames':
647 loss = float(self._loss)
649 raise Exception('Loss acceptance type not supported')
651 if loss > float(loss_acceptance):
652 raise Exception("Traffic loss {} above loss acceptance: {}".format(
653 loss, loss_acceptance))
655 def set_rate_provider_defaults(self, frame_size, traffic_type,
657 """Store values accessed by measure().
659 :param frame_size: Frame size identifier or value [B].
660 :param traffic_type: Module name as a traffic type identifier.
661 See resources/traffic_profiles/trex for implemented modules.
662 :param warmup_time: Traffic duration before measurement starts [s].
663 :type frame_size: str or int
664 :type traffic_type: str
665 :type warmup_time: float
667 self.frame_size = frame_size
668 self.traffic_type = str(traffic_type)
669 self.warmup_time = float(warmup_time)
671 def measure(self, duration, transmit_rate):
672 """Run bi-directional measurement, parse and return results.
674 :param duration: Trial duration [s].
675 :param transmit_rate: Target bidirectional transmit rate [pps].
676 :type duration: float
677 :type transmit_rate: float
678 :returns: Structure containing the result of the measurement.
679 :rtype: ReceiveRateMeasurement
680 :raises RuntimeError: If TG is not set, or if node is not TG,
681 or if subtype is not specified.
682 :raises NotImplementedError: If TG is not supported.
684 duration = float(duration)
685 transmit_rate = float(transmit_rate)
686 # Trex needs target Tr per stream, but reports aggregate Tx and Dx.
687 unit_rate = str(transmit_rate / 2.0) + "pps"
688 self.send_traffic_on_tg(
689 duration, unit_rate, self.frame_size, self.traffic_type,
690 self.warmup_time, latency=True)
691 transmit_count = int(self.get_sent())
692 loss_count = int(self.get_loss())
693 measurement = ReceiveRateMeasurement(
694 duration, transmit_rate, transmit_count, loss_count)
695 measurement.latency = self.get_latency_int()
699 class OptimizedSearch(object):
700 """Class to be imported as Robot Library, containing a single keyword."""
703 def perform_optimized_ndrpdr_search(
704 frame_size, traffic_type, minimum_transmit_rate,
705 maximum_transmit_rate, packet_loss_ratio=0.005,
706 final_relative_width=0.005, final_trial_duration=30.0,
707 initial_trial_duration=1.0, number_of_intermediate_phases=2,
708 timeout=720.0, doublings=1):
709 """Setup initialized TG, perform optimized search, return intervals.
711 :param frame_size: Frame size identifier or value [B].
712 :param traffic_type: Module name as a traffic type identifier.
713 See resources/traffic_profiles/trex for implemented modules.
714 :param minimum_transmit_rate: Minimal bidirectional
715 target transmit rate [pps].
716 :param maximum_transmit_rate: Maximal bidirectional
717 target transmit rate [pps].
718 :param packet_loss_ratio: Fraction of packets lost, for PDR [1].
719 :param final_relative_width: Final lower bound transmit rate
720 cannot be more distant that this multiple of upper bound [1].
721 :param final_trial_duration: Trial duration for the final phase [s].
722 :param initial_trial_duration: Trial duration for the initial phase
723 and also for the first intermediate phase [s].
724 :param number_of_intermediate_phases: Number of intermediate phases
725 to perform before the final phase [1].
726 :param timeout: The search will fail itself when not finished
727 before this overall time [s].
728 :param doublings: How many doublings to do in external search step.
729 Default 1 is suitable for fairly stable tests,
730 less stable tests might get better overal duration with 2 or more.
731 :type frame_size: str or int
732 :type traffic_type: str
733 :type minimum_transmit_rate: float
734 :type maximum_transmit_rate: float
735 :type packet_loss_ratio: float
736 :type final_relative_width: float
737 :type final_trial_duration: float
738 :type initial_trial_duration: float
739 :type number_of_intermediate_phases: int
742 :returns: Structure containing narrowed down NDR and PDR intervals
743 and their measurements.
745 :raises RuntimeError: If total duration is larger than timeout.
747 # we need instance of TrafficGenerator instantiated by Robot Framework
748 # to be able to use trex_stl-*()
749 tg_instance = BuiltIn().get_library_instance(
750 'resources.libraries.python.TrafficGenerator')
751 tg_instance.set_rate_provider_defaults(frame_size, traffic_type)
752 algorithm = MultipleLossRatioSearch(
753 measurer=tg_instance, final_trial_duration=final_trial_duration,
754 final_relative_width=final_relative_width,
755 number_of_intermediate_phases=number_of_intermediate_phases,
756 initial_trial_duration=initial_trial_duration, timeout=timeout,
758 result = algorithm.narrow_down_ndr_and_pdr(
759 minimum_transmit_rate, maximum_transmit_rate, packet_loss_ratio)
763 def perform_soak_search(
764 frame_size, traffic_type, minimum_transmit_rate,
765 maximum_transmit_rate, plr_target=1e-7, tdpt=0.2,
766 initial_count=50, timeout=1800.0):
767 """Setup initialized TG, perform soak search, return avg and stdev.
769 :param frame_size: Frame size identifier or value [B].
770 :param traffic_type: Module name as a traffic type identifier.
771 See resources/traffic_profiles/trex for implemented modules.
772 :param minimum_transmit_rate: Minimal bidirectional
773 target transmit rate [pps].
774 :param maximum_transmit_rate: Maximal bidirectional
775 target transmit rate [pps].
776 :param plr_target: Fraction of packets lost to achieve [1].
777 :param tdpt: Trial duration per trial.
778 The algorithm linearly increases trial duration with trial number,
779 this is the increment between succesive trials, in seconds.
780 :param initial_count: Offset to apply before the first trial.
781 For example initial_count=50 makes first trial to be 51*tdpt long.
782 This is needed because initial "search" phase of integrator
783 takes significant time even without any trial results.
784 :param timeout: The search will stop after this overall time [s].
785 :type frame_size: str or int
786 :type traffic_type: str
787 :type minimum_transmit_rate: float
788 :type maximum_transmit_rate: float
789 :type plr_target: float
790 :type initial_count: int
792 :returns: Average and stdev of estimated bidirectional rate giving PLR.
793 :rtype: 2-tuple of float
795 tg_instance = BuiltIn().get_library_instance(
796 'resources.libraries.python.TrafficGenerator')
797 tg_instance.set_rate_provider_defaults(frame_size, traffic_type)
798 algorithm = PLRsearch(
799 measurer=tg_instance, trial_duration_per_trial=tdpt,
800 packet_loss_ratio_target=plr_target,
801 trial_number_offset=initial_count, timeout=timeout)
802 result = algorithm.search(minimum_transmit_rate, maximum_transmit_rate)