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(
462 self, duration, rate, framesize, traffic_type, async_call=False,
463 latency=False, warmup_time=5.0, tx_port=0, rx_port=1):
464 """Execute script on remote node over ssh to start unidirection traffic.
465 The purpose of this function is to support performance test that need to
466 measure unidirectional traffic, e.g. Load balancer maglev mode and l3dsr
469 :param duration: Time expresed in seconds for how long to send traffic.
470 :param rate: Traffic rate expressed with units (pps, %)
471 :param framesize: L2 frame size to send (without padding and IPG).
472 :param traffic_type: Module name as a traffic type identifier.
473 See resources/traffic_profiles/trex for implemented modules.
474 :param latency: With latency measurement.
475 :param async_call: If enabled then don't wait for all incomming trafic.
476 :param warmup_time: Warmup time period.
477 :param tx_port: Traffic generator transmit port.
478 :param rx_port: Traffic generator receive port.
479 :type duration: float
482 :type traffic_type: str
484 :type async_call: bool
485 :type warmup_time: float
486 :type tx_port: integer
487 :type rx_port: integer
488 :raises RuntimeError: In case of TG driver issue.
491 ssh.connect(self._node)
493 _latency = "--latency" if latency else ""
494 _async = "--async" if async_call else ""
496 profile_path = ("{0}/resources/traffic_profiles/trex/"
497 "{1}.py".format(Constants.REMOTE_FW_DIR,
499 (ret, stdout, _) = ssh.exec_command(
501 "'{0}/resources/tools/trex/trex_stateless_profile.py "
511 "{10}'". # --unidirection
512 format(Constants.REMOTE_FW_DIR, profile_path, duration, framesize,
513 rate, warmup_time, tx_port, rx_port, _async, _latency,
515 timeout=float(duration) + 60)
518 raise RuntimeError('TRex unidirection runtime error')
521 self._received = None
526 # last line from console output
527 line = stdout.splitlines()[-1]
530 logger.info('TrafficGen result: {0}'.format(self._result))
532 self._received = self._result.split(', ')[1].split('=')[1]
533 self._sent = self._result.split(', ')[2].split('=')[1]
534 self._loss = self._result.split(', ')[3].split('=')[1]
536 self._latency.append(self._result.split(', ')[4].split('=')[1])
538 def stop_traffic_on_tg(self):
539 """Stop all traffic on TG.
542 :raises RuntimeError: If TG is not set.
544 if self._node is None:
545 raise RuntimeError("TG is not set")
546 if self._node['subtype'] == NodeSubTypeTG.TREX:
547 self.trex_stl_stop_remote_exec(self._node)
549 def send_traffic_on_tg(
550 self, duration, rate, framesize, traffic_type, warmup_time=5,
551 async_call=False, latency=True, unidirection=False, tx_port=0,
553 """Send traffic from all configured interfaces on TG.
555 :param duration: Duration of test traffic generation in seconds.
556 :param rate: Offered load per interface (e.g. 1%, 3gbps, 4mpps, ...).
557 :param framesize: Frame size (L2) in Bytes.
558 :param traffic_type: Module name as a traffic type identifier.
559 See resources/traffic_profiles/trex for implemented modules.
560 :param warmup_time: Warmup phase in seconds.
561 :param async_call: Async mode.
562 :param latency: With latency measurement.
563 :param unidirection: Traffic is unidirectional.
564 :param tx_port: Traffic generator transmit port.
565 :param rx_port: Traffic generator receive port.
569 :type traffic_type: str
570 :type warmup_time: float
571 :type async_call: bool
573 :type unidirection: bool
574 :type tx_port: integer
575 :type rx_port: integer
578 :raises RuntimeError: If TG is not set, or if node is not TG,
579 or if subtype is not specified.
580 :raises NotImplementedError: If TG is not supported.
585 raise RuntimeError("TG is not set")
587 if node['type'] != NodeType.TG:
588 raise RuntimeError('Node type is not a TG')
590 if node['subtype'] is None:
591 raise RuntimeError('TG subtype not defined')
592 elif node['subtype'] == NodeSubTypeTG.TREX:
594 self.trex_stl_start_unidirection(duration, rate, framesize,
595 traffic_type, tx_port,
596 rx_port, async_call, latency,
599 self.trex_stl_start_remote_exec(duration, rate, framesize,
600 traffic_type, async_call,
601 latency, warmup_time)
603 raise NotImplementedError("TG subtype not supported")
607 def no_traffic_loss_occurred(self):
608 """Fail if loss occurred in traffic run.
611 :raises Exception: If loss occured.
613 if self._loss is None:
614 raise RuntimeError('The traffic generation has not been issued')
615 if self._loss != '0':
616 raise RuntimeError('Traffic loss occurred: {0}'.format(self._loss))
618 def fail_if_no_traffic_forwarded(self):
619 """Fail if no traffic forwarded.
622 :raises Exception: If no traffic forwarded.
624 if self._received is None:
625 raise RuntimeError('The traffic generation has not been issued')
626 if self._received == '0':
627 raise RuntimeError('No traffic forwarded')
629 def partial_traffic_loss_accepted(self, loss_acceptance,
630 loss_acceptance_type):
631 """Fail if loss is higher then accepted in traffic run.
633 :param loss_acceptance: Permitted drop ratio or frames count.
634 :param loss_acceptance_type: Type of permitted loss.
635 :type loss_acceptance: float
636 :type loss_acceptance_type: LossAcceptanceType
638 :raises Exception: If loss is above acceptance criteria.
640 if self._loss is None:
641 raise Exception('The traffic generation has not been issued')
643 if loss_acceptance_type == 'percentage':
644 loss = (float(self._loss) / float(self._sent)) * 100
645 elif loss_acceptance_type == 'frames':
646 loss = float(self._loss)
648 raise Exception('Loss acceptance type not supported')
650 if loss > float(loss_acceptance):
651 raise Exception("Traffic loss {} above loss acceptance: {}".format(
652 loss, loss_acceptance))
654 def set_rate_provider_defaults(self, frame_size, traffic_type,
656 """Store values accessed by measure().
658 :param frame_size: Frame size identifier or value [B].
659 :param traffic_type: Module name as a traffic type identifier.
660 See resources/traffic_profiles/trex for implemented modules.
661 :param warmup_time: Traffic duration before measurement starts [s].
662 :type frame_size: str or int
663 :type traffic_type: str
664 :type warmup_time: float
666 self.frame_size = frame_size
667 self.traffic_type = str(traffic_type)
668 self.warmup_time = float(warmup_time)
670 def measure(self, duration, transmit_rate):
671 """Run bi-directional measurement, parse and return results.
673 :param duration: Trial duration [s].
674 :param transmit_rate: Target bidirectional transmit rate [pps].
675 :type duration: float
676 :type transmit_rate: float
677 :returns: Structure containing the result of the measurement.
678 :rtype: ReceiveRateMeasurement
679 :raises RuntimeError: If TG is not set, or if node is not TG,
680 or if subtype is not specified.
681 :raises NotImplementedError: If TG is not supported.
683 duration = float(duration)
684 transmit_rate = float(transmit_rate)
685 # Trex needs target Tr per stream, but reports aggregate Tx and Dx.
686 unit_rate = str(transmit_rate / 2.0) + "pps"
687 self.send_traffic_on_tg(
688 duration, unit_rate, self.frame_size, self.traffic_type,
689 warmup_time=self.warmup_time, latency=True)
690 transmit_count = int(self.get_sent())
691 loss_count = int(self.get_loss())
692 measurement = ReceiveRateMeasurement(
693 duration, transmit_rate, transmit_count, loss_count)
694 measurement.latency = self.get_latency_int()
698 class OptimizedSearch(object):
699 """Class to be imported as Robot Library, containing a single keyword."""
702 def perform_optimized_ndrpdr_search(
703 frame_size, traffic_type, minimum_transmit_rate,
704 maximum_transmit_rate, packet_loss_ratio=0.005,
705 final_relative_width=0.005, final_trial_duration=30.0,
706 initial_trial_duration=1.0, number_of_intermediate_phases=2,
707 timeout=720.0, doublings=1):
708 """Setup initialized TG, perform optimized search, return intervals.
710 :param frame_size: Frame size identifier or value [B].
711 :param traffic_type: Module name as a traffic type identifier.
712 See resources/traffic_profiles/trex for implemented modules.
713 :param minimum_transmit_rate: Minimal bidirectional
714 target transmit rate [pps].
715 :param maximum_transmit_rate: Maximal bidirectional
716 target transmit rate [pps].
717 :param packet_loss_ratio: Fraction of packets lost, for PDR [1].
718 :param final_relative_width: Final lower bound transmit rate
719 cannot be more distant that this multiple of upper bound [1].
720 :param final_trial_duration: Trial duration for the final phase [s].
721 :param initial_trial_duration: Trial duration for the initial phase
722 and also for the first intermediate phase [s].
723 :param number_of_intermediate_phases: Number of intermediate phases
724 to perform before the final phase [1].
725 :param timeout: The search will fail itself when not finished
726 before this overall time [s].
727 :param doublings: How many doublings to do in external search step.
728 Default 1 is suitable for fairly stable tests,
729 less stable tests might get better overal duration with 2 or more.
730 :type frame_size: str or int
731 :type traffic_type: str
732 :type minimum_transmit_rate: float
733 :type maximum_transmit_rate: float
734 :type packet_loss_ratio: float
735 :type final_relative_width: float
736 :type final_trial_duration: float
737 :type initial_trial_duration: float
738 :type number_of_intermediate_phases: int
741 :returns: Structure containing narrowed down NDR and PDR intervals
742 and their measurements.
744 :raises RuntimeError: If total duration is larger than timeout.
746 # we need instance of TrafficGenerator instantiated by Robot Framework
747 # to be able to use trex_stl-*()
748 tg_instance = BuiltIn().get_library_instance(
749 'resources.libraries.python.TrafficGenerator')
750 tg_instance.set_rate_provider_defaults(frame_size, traffic_type)
751 algorithm = MultipleLossRatioSearch(
752 measurer=tg_instance, final_trial_duration=final_trial_duration,
753 final_relative_width=final_relative_width,
754 number_of_intermediate_phases=number_of_intermediate_phases,
755 initial_trial_duration=initial_trial_duration, timeout=timeout,
757 result = algorithm.narrow_down_ndr_and_pdr(
758 minimum_transmit_rate, maximum_transmit_rate, packet_loss_ratio)
762 def perform_soak_search(
763 frame_size, traffic_type, minimum_transmit_rate,
764 maximum_transmit_rate, plr_target=1e-7, tdpt=0.2,
765 initial_count=50, timeout=1800.0):
766 """Setup initialized TG, perform soak search, return avg and stdev.
768 :param frame_size: Frame size identifier or value [B].
769 :param traffic_type: Module name as a traffic type identifier.
770 See resources/traffic_profiles/trex for implemented modules.
771 :param minimum_transmit_rate: Minimal bidirectional
772 target transmit rate [pps].
773 :param maximum_transmit_rate: Maximal bidirectional
774 target transmit rate [pps].
775 :param plr_target: Fraction of packets lost to achieve [1].
776 :param tdpt: Trial duration per trial.
777 The algorithm linearly increases trial duration with trial number,
778 this is the increment between succesive trials, in seconds.
779 :param initial_count: Offset to apply before the first trial.
780 For example initial_count=50 makes first trial to be 51*tdpt long.
781 This is needed because initial "search" phase of integrator
782 takes significant time even without any trial results.
783 :param timeout: The search will stop after this overall time [s].
784 :type frame_size: str or int
785 :type traffic_type: str
786 :type minimum_transmit_rate: float
787 :type maximum_transmit_rate: float
788 :type plr_target: float
789 :type initial_count: int
791 :returns: Average and stdev of estimated bidirectional rate giving PLR.
792 :rtype: 2-tuple of float
794 tg_instance = BuiltIn().get_library_instance(
795 'resources.libraries.python.TrafficGenerator')
796 tg_instance.set_rate_provider_defaults(frame_size, traffic_type)
797 algorithm = PLRsearch(
798 measurer=tg_instance, trial_duration_per_trial=tdpt,
799 packet_loss_ratio_target=plr_target,
800 trial_number_offset=initial_count, timeout=timeout)
801 result = algorithm.search(minimum_transmit_rate, maximum_transmit_rate)