Pylint fixes
[csit.git] / resources / libraries / python / TrafficGenerator.py
1 # Copyright (c) 2016 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:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
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.
13
14 """Performance testing traffic generator library."""
15
16 from robot.api import logger
17 from robot.libraries.BuiltIn import BuiltIn
18
19 from resources.libraries.python.constants import Constants
20 from resources.libraries.python.ssh import SSH
21 from resources.libraries.python.topology import NodeType
22 from resources.libraries.python.topology import NodeSubTypeTG
23 from resources.libraries.python.topology import Topology
24 from resources.libraries.python.DropRateSearch import DropRateSearch
25
26 __all__ = ['TrafficGenerator', 'TGDropRateSearchImpl']
27
28
29 class TGDropRateSearchImpl(DropRateSearch):
30     """Drop Rate Search implementation."""
31
32     def __init__(self):
33         super(TGDropRateSearchImpl, self).__init__()
34
35     def measure_loss(self, rate, frame_size, loss_acceptance,
36                      loss_acceptance_type, traffic_type):
37         """Runs the traffic and evaluate the measured results.
38
39         :param rate: Offered traffic load.
40         :param frame_size: Size of frame.
41         :param loss_acceptance: Permitted drop ratio or frames count.
42         :param loss_acceptance_type: Type of permitted loss.
43         :param traffic_type: Traffic profile ([2,3]-node-L[2,3], ...).
44         :type rate: int
45         :type frame_size: str
46         :type loss_acceptance: float
47         :type loss_acceptance_type: LossAcceptanceType
48         :type traffic_type: str
49         :returns: Drop threshold exceeded? (True/False)
50         :rtype: bool
51         :raises: NotImplementedError if TG is not supported.
52         :raises: RuntimeError if TG is not specified.
53         """
54         # we need instance of TrafficGenerator instantiated by Robot Framework
55         # to be able to use trex_stl-*()
56         tg_instance = BuiltIn().get_library_instance(
57             'resources.libraries.python.TrafficGenerator')
58
59         if tg_instance.node['subtype'] is None:
60             raise RuntimeError('TG subtype not defined')
61         elif tg_instance.node['subtype'] == NodeSubTypeTG.TREX:
62             unit_rate = str(rate) + self.get_rate_type_str()
63             tg_instance.trex_stl_start_remote_exec(self.get_duration(),
64                                                    unit_rate, frame_size,
65                                                    traffic_type)
66             loss = tg_instance.get_loss()
67             sent = tg_instance.get_sent()
68             if self.loss_acceptance_type_is_percentage():
69                 loss = (float(loss) / float(sent)) * 100
70
71             logger.trace("comparing: {} < {} {}".format(loss,
72                                                         loss_acceptance,
73                                                         loss_acceptance_type))
74             if float(loss) > float(loss_acceptance):
75                 return False
76             else:
77                 return True
78         else:
79             raise NotImplementedError("TG subtype not supported")
80
81     def get_latency(self):
82         """Returns min/avg/max latency.
83
84         :returns: Latency stats.
85         :rtype: list
86         """
87         tg_instance = BuiltIn().get_library_instance(
88             'resources.libraries.python.TrafficGenerator')
89         return tg_instance.get_latency_int()
90
91
92 class TrafficGenerator(object):
93     """Traffic Generator."""
94
95     # use one instance of TrafficGenerator for all tests in test suite
96     ROBOT_LIBRARY_SCOPE = 'TEST SUITE'
97
98     def __init__(self):
99         self._result = None
100         self._loss = None
101         self._sent = None
102         self._latency = None
103         self._received = None
104         self._node = None
105         # T-REX interface order mapping
106         self._ifaces_reordered = 0
107
108     @property
109     def node(self):
110         """Getter.
111
112         :returns: Traffic generator node.
113         :rtype: dict
114         """
115         return self._node
116
117     def get_loss(self):
118         """Return number of lost packets.
119
120         :returns: Number of lost packets.
121         :rtype: str
122         """
123         return self._loss
124
125     def get_sent(self):
126         """Return number of sent packets.
127
128         :returns: Number of sent packets.
129         :rtype: str
130         """
131         return self._sent
132
133     def get_received(self):
134         """Return number of received packets.
135
136         :returns: Number of received packets.
137         :rtype: str
138         """
139         return self._received
140
141     def get_latency_int(self):
142         """Return rounded min/avg/max latency.
143
144         :returns: Latency stats.
145         :rtype: list
146         """
147         return self._latency
148
149     def initialize_traffic_generator(self, tg_node, tg_if1, tg_if2,
150                                      tg_if1_adj_node, tg_if1_adj_if,
151                                      tg_if2_adj_node, tg_if2_adj_if,
152                                      test_type):
153         """TG initialization.
154
155         :param tg_node: Traffic generator node.
156         :param tg_if1: TG - name of first interface.
157         :param tg_if2: TG - name of second interface.
158         :param tg_if1_adj_node: TG if1 adjecent node.
159         :param tg_if1_adj_if: TG if1 adjecent interface.
160         :param tg_if2_adj_node: TG if2 adjecent node.
161         :param tg_if2_adj_if: TG if2 adjecent interface.
162         :param test_type: 'L2' or 'L3' - src/dst MAC address.
163         :type tg_node: dict
164         :type tg_if1: str
165         :type tg_if2: str
166         :type tg_if1_adj_node: dict
167         :type tg_if1_adj_if: str
168         :type tg_if2_adj_node: dict
169         :type tg_if2_adj_if: str
170         :type test_type: str
171         :returns: nothing
172         :raises: RuntimeError in case of issue during initialization.
173         """
174
175         topo = Topology()
176
177         if tg_node['type'] != NodeType.TG:
178             raise RuntimeError('Node type is not a TG')
179         self._node = tg_node
180
181         if tg_node['subtype'] == NodeSubTypeTG.TREX:
182             trex_path = "/opt/trex-core-2.09"
183
184             ssh = SSH()
185             ssh.connect(tg_node)
186
187             (ret, stdout, stderr) = ssh.exec_command(
188                 "sudo -E sh -c '{}/resources/tools/t-rex/"
189                 "t-rex-installer.sh'".format(Constants.REMOTE_FW_DIR),
190                 timeout=1800)
191             if int(ret) != 0:
192                 logger.error('trex installation failed: {0}'.format(
193                     stdout + stderr))
194                 raise RuntimeError('Installation of TG failed')
195
196             if1_pci = topo.get_interface_pci_addr(tg_node, tg_if1)
197             if2_pci = topo.get_interface_pci_addr(tg_node, tg_if2)
198             if1_mac = topo.get_interface_mac(tg_node, tg_if1)
199             if2_mac = topo.get_interface_mac(tg_node, tg_if2)
200
201             if test_type == 'L2':
202                 if1_adj_mac = if2_mac
203                 if2_adj_mac = if1_mac
204             elif test_type == 'L3':
205                 if1_adj_mac = topo.get_interface_mac(tg_if1_adj_node,
206                                                      tg_if1_adj_if)
207                 if2_adj_mac = topo.get_interface_mac(tg_if2_adj_node,
208                                                      tg_if2_adj_if)
209             else:
210                 raise Exception("test_type unknown")
211
212             if min(if1_pci, if2_pci) != if1_pci:
213                 if1_mac, if2_mac = if2_mac, if1_mac
214                 if1_pci, if2_pci = if2_pci, if1_pci
215                 if1_adj_mac, if2_adj_mac = if2_adj_mac, if1_adj_mac
216                 self._ifaces_reordered = 1
217
218             if1_mac_hex = "0x"+if1_mac.replace(":", ",0x")
219             if2_mac_hex = "0x"+if2_mac.replace(":", ",0x")
220             if1_adj_mac_hex = "0x"+if1_adj_mac.replace(":", ",0x")
221             if2_adj_mac_hex = "0x"+if2_adj_mac.replace(":", ",0x")
222
223             (ret, stdout, stderr) = ssh.exec_command(
224                 "sudo sh -c 'cat << EOF > /etc/trex_cfg.yaml\n"
225                 "- port_limit      : 2\n"
226                 "  version         : 2\n"
227                 "  interfaces      : [\"{}\",\"{}\"]\n"
228                 "  port_info       :\n"
229                 "          - dest_mac        :   [{}]\n"
230                 "            src_mac         :   [{}]\n"
231                 "          - dest_mac        :   [{}]\n"
232                 "            src_mac         :   [{}]\n"
233                 "EOF'"\
234                 .format(if1_pci, if2_pci,
235                         if1_adj_mac_hex, if1_mac_hex,
236                         if2_adj_mac_hex, if2_mac_hex))
237             if int(ret) != 0:
238                 logger.error("failed to create t-rex config: {}"\
239                 .format(stdout + stderr))
240                 raise RuntimeError('trex config generation error')
241
242             max_startup_retries = 3
243             while max_startup_retries > 0:
244                 # kill T-rex only if it is already running
245                 (ret, _, _) = ssh.exec_command(
246                     "sh -c 'pgrep t-rex && sudo pkill t-rex'")
247
248                 # configure T-rex
249                 (ret, stdout, stderr) = ssh.exec_command(
250                     "sh -c 'cd {0}/scripts/ && sudo ./trex-cfg'"\
251                     .format(trex_path))
252                 if int(ret) != 0:
253                     logger.error('trex-cfg failed: {0}'.format(stdout + stderr))
254                     raise RuntimeError('trex-cfg failed')
255
256                 # start T-rex
257                 (ret, _, _) = ssh.exec_command(
258                     "sh -c 'cd {0}/scripts/ && "
259                     "sudo nohup ./t-rex-64 -i -c 7 --iom 0 > /dev/null 2>&1 &'"
260                     "> /dev/null"\
261                     .format(trex_path))
262                 if int(ret) != 0:
263                     raise RuntimeError('t-rex-64 startup failed')
264
265                 # get T-rex server info
266                 (ret, _, _) = ssh.exec_command(
267                     "sh -c '{0}/resources/tools/t-rex/t-rex-server-info.py'"\
268                     .format(Constants.REMOTE_FW_DIR),
269                     timeout=120)
270                 if int(ret) == 0:
271                     # If we get info T-rex is running
272                     return
273                 # try again
274                 max_startup_retries -= 1
275             # after max retries T-rex is still not responding to API
276             # critical error occured
277             raise RuntimeError('t-rex-64 startup failed')
278
279
280     @staticmethod
281     def teardown_traffic_generator(node):
282         """TG teardown.
283
284         :param node: Traffic generator node.
285         :type node: dict
286         :returns: nothing
287         :raises: RuntimeError if T-rex teardown failed.
288         :raises: RuntimeError if node type is not a TG.
289         """
290         if node['type'] != NodeType.TG:
291             raise RuntimeError('Node type is not a TG')
292         if node['subtype'] == NodeSubTypeTG.TREX:
293             ssh = SSH()
294             ssh.connect(node)
295             (ret, stdout, stderr) = ssh.exec_command(
296                 "sh -c 'sudo pkill t-rex'")
297             if int(ret) != 0:
298                 logger.error('pkill t-rex failed: {0}'.format(stdout + stderr))
299                 raise RuntimeError('pkill t-rex failed')
300
301     @staticmethod
302     def trex_stl_stop_remote_exec(node):
303         """Execute script on remote node over ssh to stop running traffic.
304
305         :param node: T-REX generator node.
306         :type node: dict
307         :returns: Nothing
308         :raises: RuntimeError if stop traffic script fails.
309         """
310         ssh = SSH()
311         ssh.connect(node)
312
313         (ret, stdout, stderr) = ssh.exec_command(
314             "sh -c '{}/resources/tools/t-rex/"
315             "t-rex-stateless-stop.py'".format(Constants.REMOTE_FW_DIR))
316         logger.trace(ret)
317         logger.trace(stdout)
318         logger.trace(stderr)
319
320         if int(ret) != 0:
321             raise RuntimeError('T-rex stateless runtime error')
322
323     def trex_stl_start_remote_exec(self, duration, rate, framesize,
324                                    traffic_type, async_call=False,
325                                    latency=True, warmup_time=5):
326         """Execute script on remote node over ssh to start traffic.
327
328         :param duration: Time expresed in seconds for how long to send traffic.
329         :param rate: Traffic rate expressed with units (pps, %)
330         :param framesize: L2 frame size to send (without padding and IPG).
331         :param traffic_type: Traffic profile.
332         :param async_call: If enabled then don't wait for all incomming trafic.
333         :param latency: With latency measurement.
334         :param warmup_time: Warmup time period.
335         :type duration: int
336         :type rate: str
337         :type framesize: int
338         :type traffic_type: str
339         :type async_call: bool
340         :type latency: bool
341         :type warmup_time: int
342         :returns: Nothing
343         :raises: NotImplementedError if traffic type is not supported.
344         :raises: RuntimeError in case of TG driver issue.
345         """
346         ssh = SSH()
347         ssh.connect(self._node)
348
349         _p0 = 1
350         _p1 = 2
351         _async = "--async" if async_call else ""
352         _latency = "--latency" if latency else ""
353
354         if self._ifaces_reordered != 0:
355             _p0, _p1 = _p1, _p0
356
357         if traffic_type in ["3-node-xconnect", "3-node-bridge"]:
358             (ret, stdout, stderr) = ssh.exec_command(
359                 "sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
360                 "--duration={1} -r {2} -s {3} "
361                 "--p{4}_src_start_ip 10.10.10.1 "
362                 "--p{4}_src_end_ip 10.10.10.254 "
363                 "--p{4}_dst_start_ip 20.20.20.1 "
364                 "--p{5}_src_start_ip 20.20.20.1 "
365                 "--p{5}_src_end_ip 20.20.20.254 "
366                 "--p{5}_dst_start_ip 10.10.10.1 "
367                 "{6} {7} --warmup_time={8}'".format(Constants.REMOTE_FW_DIR,
368                                                     duration, rate, framesize,
369                                                     _p0, _p1, _async, _latency,
370                                                     warmup_time),
371                 timeout=int(duration)+60)
372         elif traffic_type in ["3-node-IPv4"]:
373             (ret, stdout, stderr) = ssh.exec_command(
374                 "sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
375                 "--duration={1} -r {2} -s {3} "
376                 "--p{4}_src_start_ip 10.10.10.2 "
377                 "--p{4}_src_end_ip 10.10.10.254 "
378                 "--p{4}_dst_start_ip 20.20.20.2 "
379                 "--p{5}_src_start_ip 20.20.20.2 "
380                 "--p{5}_src_end_ip 20.20.20.254 "
381                 "--p{5}_dst_start_ip 10.10.10.2 "
382                 "{6} {7} --warmup_time={8}'".format(Constants.REMOTE_FW_DIR,
383                                                     duration, rate, framesize,
384                                                     _p0, _p1, _async, _latency,
385                                                     warmup_time),
386                 timeout=int(duration)+60)
387         elif traffic_type in ["3-node-IPv4-dst-10000"]:
388             (ret, stdout, stderr) = ssh.exec_command(
389                 "sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
390                 "--duration={1} -r {2} -s {3} "
391                 "--p{4}_src_start_ip 10.0.0.1 "
392                 "--p{4}_dst_start_ip 20.0.0.0 "
393                 "--p{4}_dst_end_ip 20.0.39.15 "
394                 "--p{5}_src_start_ip 20.0.0.1 "
395                 "--p{5}_dst_start_ip 10.0.0.0 "
396                 "--p{5}_dst_end_ip 10.0.39.15 "
397                 "{6} {7} --warmup_time={8}'".format(Constants.REMOTE_FW_DIR,
398                                                     duration, rate, framesize,
399                                                     _p0, _p1, _async, _latency,
400                                                     warmup_time),
401                 timeout=int(duration)+60)
402         elif traffic_type in ["3-node-IPv4-dst-100000"]:
403             (ret, stdout, stderr) = ssh.exec_command(
404                 "sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
405                 "--duration={1} -r {2} -s {3} "
406                 "--p{4}_src_start_ip 10.0.0.1 "
407                 "--p{4}_dst_start_ip 20.0.0.0 "
408                 "--p{4}_dst_end_ip 20.1.134.159 "
409                 "--p{5}_src_start_ip 20.0.0.1 "
410                 "--p{5}_dst_start_ip 10.0.0.0 "
411                 "--p{5}_dst_end_ip 10.1.134.159 "
412                 "{6} {7} --warmup_time={8}'".format(Constants.REMOTE_FW_DIR,
413                                                     duration, rate, framesize,
414                                                     _p0, _p1, _async, _latency,
415                                                     warmup_time),
416                 timeout=int(duration)+60)
417         elif traffic_type in ["3-node-IPv4-dst-1000000"]:
418             (ret, stdout, stderr) = ssh.exec_command(
419                 "sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
420                 "--duration={1} -r {2} -s {3} "
421                 "--p{4}_src_start_ip 10.0.0.1 "
422                 "--p{4}_dst_start_ip 20.0.0.0 "
423                 "--p{4}_dst_end_ip 20.15.66.63 "
424                 "--p{5}_src_start_ip 20.0.0.1 "
425                 "--p{5}_dst_start_ip 10.0.0.0 "
426                 "--p{5}_dst_end_ip 10.15.66.63 "
427                 "{6} {7} --warmup_time={8}'".format(Constants.REMOTE_FW_DIR,
428                                                     duration, rate, framesize,
429                                                     _p0, _p1, _async, _latency,
430                                                     warmup_time),
431                 timeout=int(duration)+60)
432         elif traffic_type in ["3-node-IPv6"]:
433             (ret, stdout, stderr) = ssh.exec_command(
434                 "sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
435                 "--duration={1} -r {2} -s {3} -6 "
436                 "--p{4}_src_start_ip 2001:1::2 "
437                 "--p{4}_src_end_ip 2001:1::FE "
438                 "--p{4}_dst_start_ip 2001:2::2 "
439                 "--p{5}_src_start_ip 2001:2::2 "
440                 "--p{5}_src_end_ip 2001:2::FE "
441                 "--p{5}_dst_start_ip 2001:1::2 "
442                 "{6} {7} --warmup_time={8}'".format(Constants.REMOTE_FW_DIR,
443                                                     duration, rate, framesize,
444                                                     _p0, _p1, _async, _latency,
445                                                     warmup_time),
446                 timeout=int(duration)+60)
447         elif traffic_type in ["3-node-IPv6-dst-10000"]:
448             (ret, stdout, stderr) = ssh.exec_command(
449                 "sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
450                 "--duration={1} -r {2} -s {3} -6 "
451                 "--p{4}_src_start_ip 2001:1::1 "
452                 "--p{4}_dst_start_ip 2001:2::0 "
453                 "--p{4}_dst_end_ip 2001:2::270F "
454                 "--p{5}_src_start_ip 2001:2::1 "
455                 "--p{5}_dst_start_ip 2001:1::0 "
456                 "--p{5}_dst_end_ip 2001:1::270F "
457                 "{6} {7} --warmup_time={8}'".format(Constants.REMOTE_FW_DIR,
458                                                     duration, rate, framesize,
459                                                     _p0, _p1, _async, _latency,
460                                                     warmup_time),
461                 timeout=int(duration)+60)
462         elif traffic_type in ["3-node-IPv6-dst-100000"]:
463             (ret, stdout, stderr) = ssh.exec_command(
464                 "sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
465                 "--duration={1} -r {2} -s {3} -6 "
466                 "--p{4}_src_start_ip 2001:1::1 "
467                 "--p{4}_dst_start_ip 2001:2::0 "
468                 "--p{4}_dst_end_ip 2001:2::1:869F "
469                 "--p{5}_src_start_ip 2001:2::1 "
470                 "--p{5}_dst_start_ip 2001:1::0 "
471                 "--p{5}_dst_end_ip 2001:1::1:869F "
472                 "{6} {7} --warmup_time={8}'".format(Constants.REMOTE_FW_DIR,
473                                                     duration, rate, framesize,
474                                                     _p0, _p1, _async, _latency,
475                                                     warmup_time),
476                 timeout=int(duration)+60)
477         elif traffic_type in ["3-node-IPv6-dst-1000000"]:
478             (ret, stdout, stderr) = ssh.exec_command(
479                 "sh -c '{0}/resources/tools/t-rex/t-rex-stateless.py "
480                 "--duration={1} -r {2} -s {3} -6 "
481                 "--p{4}_src_start_ip 2001:1::1 "
482                 "--p{4}_dst_start_ip 2001:2::0 "
483                 "--p{4}_dst_end_ip 2001:2::F:423F "
484                 "--p{5}_src_start_ip 2001:2::1 "
485                 "--p{5}_dst_start_ip 2001:1::0 "
486                 "--p{5}_dst_end_ip 2001:1::F:423F "
487                 "{6} {7} --warmup_time={8}'".format(Constants.REMOTE_FW_DIR,
488                                                     duration, rate, framesize,
489                                                     _p0, _p1, _async, _latency,
490                                                     warmup_time),
491                 timeout=int(duration)+60)
492         else:
493             raise NotImplementedError('Unsupported traffic type')
494
495         logger.trace(ret)
496         logger.trace(stdout)
497         logger.trace(stderr)
498
499         if int(ret) != 0:
500             raise RuntimeError('T-rex stateless runtime error')
501         elif async_call:
502             #no result
503             self._received = None
504             self._sent = None
505             self._loss = None
506             self._latency = None
507         else:
508             # last line from console output
509             line = stdout.splitlines()[-1]
510
511             self._result = line
512             logger.info('TrafficGen result: {0}'.format(self._result))
513
514             self._received = self._result.split(', ')[1].split('=')[1]
515             self._sent = self._result.split(', ')[2].split('=')[1]
516             self._loss = self._result.split(', ')[3].split('=')[1]
517
518             self._latency = []
519             self._latency.append(self._result.split(', ')[4].split('=')[1])
520             self._latency.append(self._result.split(', ')[5].split('=')[1])
521
522     def stop_traffic_on_tg(self):
523         """Stop all traffic on TG.
524
525         :returns: Nothing
526         :raises: RuntimeError if TG is not set.
527         """
528         if self._node is None:
529             raise RuntimeError("TG is not set")
530         if self._node['subtype'] == NodeSubTypeTG.TREX:
531             self.trex_stl_stop_remote_exec(self._node)
532
533     def send_traffic_on_tg(self, duration, rate, framesize,
534                            traffic_type, warmup_time=5, async_call=False,
535                            latency=True):
536         """Send traffic from all configured interfaces on TG.
537
538         :param duration: Duration of test traffic generation in seconds.
539         :param rate: Offered load per interface (e.g. 1%, 3gbps, 4mpps, ...).
540         :param framesize: Frame size (L2) in Bytes.
541         :param traffic_type: Traffic profile.
542         :param warmup_time: Warmup phase in seconds.
543         :param async_call: Async mode.
544         :param latency: With latency measurement.
545         :type duration: str
546         :type rate: str
547         :type framesize: str
548         :type traffic_type: str
549         :type warmup_time: int
550         :type async_call: bool
551         :type latency: bool
552         :returns: TG output.
553         :rtype: str
554         :raises: RuntimeError if TG is not set.
555         :raises: RuntimeError if node is not TG or subtype is not specified.
556         :raises: NotImplementedError if TG is not supported.
557         """
558
559         node = self._node
560         if node is None:
561             raise RuntimeError("TG is not set")
562
563         if node['type'] != NodeType.TG:
564             raise RuntimeError('Node type is not a TG')
565
566         if node['subtype'] is None:
567             raise RuntimeError('TG subtype not defined')
568         elif node['subtype'] == NodeSubTypeTG.TREX:
569             self.trex_stl_start_remote_exec(duration, rate, framesize,
570                                             traffic_type, async_call, latency,
571                                             warmup_time=warmup_time)
572         else:
573             raise NotImplementedError("TG subtype not supported")
574
575         return self._result
576
577     def no_traffic_loss_occurred(self):
578         """Fail if loss occurred in traffic run.
579
580         :returns: nothing
581         :raises: Exception if loss occured.
582         """
583         if self._loss is None:
584             raise Exception('The traffic generation has not been issued')
585         if self._loss != '0':
586             raise Exception('Traffic loss occurred: {0}'.format(self._loss))
587
588     def partial_traffic_loss_accepted(self, loss_acceptance,
589                                       loss_acceptance_type):
590         """Fail if loss is higher then accepted in traffic run.
591
592         :param loss_acceptance: Permitted drop ratio or frames count.
593         :param loss_acceptance_type: Type of permitted loss.
594         :type loss_acceptance: float
595         :type loss_acceptance_type: LossAcceptanceType
596         :returns: nothing
597         :raises: Exception if loss is above acceptance criteria.
598         """
599         if self._loss is None:
600             raise Exception('The traffic generation has not been issued')
601
602         if loss_acceptance_type == 'percentage':
603             loss = (float(self._loss) / float(self._sent)) * 100
604         elif loss_acceptance_type == 'frames':
605             loss = float(self._loss)
606         else:
607             raise Exception('Loss acceptance type not supported')
608
609         if loss > float(loss_acceptance):
610             raise Exception("Traffic loss {} above loss acceptance: {}".format(
611                 loss, loss_acceptance))