Merge "xena_deadlock: Fix xena2544.exe deadlock"
[vswitchperf.git] / tools / pkt_gen / xena / xena.py
1 # Copyright 2016 Red Hat Inc & Xena Networks.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #   http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15 # Contributors:
16 #   Rick Alongi, Red Hat Inc.
17 #   Amit Supugade, Red Hat Inc.
18 #   Dan Amzulescu, Xena Networks
19 #   Christian Trautman, Red Hat Inc.
20
21 """
22 Xena Traffic Generator Model
23 """
24
25 # python imports
26 import binascii
27 import logging
28 import os
29 import subprocess
30 import sys
31 from time import sleep
32 import xml.etree.ElementTree as ET
33 from collections import OrderedDict
34 # scapy imports
35 import scapy.layers.inet as inet
36
37 # VSPerf imports
38 from conf import settings
39 from core.results.results_constants import ResultsConstants
40 from tools.pkt_gen.trafficgen.trafficgenhelper import (
41     TRAFFIC_DEFAULTS,
42     merge_spec)
43 from tools.pkt_gen.trafficgen.trafficgen import ITrafficGenerator
44
45 # Xena module imports
46 from tools.pkt_gen.xena.xena_json import XenaJSON
47 from tools.pkt_gen.xena.XenaDriver import (
48     aggregate_stats,
49     line_percentage,
50     XenaSocketDriver,
51     XenaManager,
52     )
53
54
55 class Xena(ITrafficGenerator):
56     """
57     Xena Traffic generator wrapper class
58     """
59     _traffic_defaults = TRAFFIC_DEFAULTS.copy()
60     _logger = logging.getLogger(__name__)
61
62     def __init__(self):
63         self.mono_pipe = None
64         self.xmanager = None
65         self._params = {}
66         self._xsocket = None
67         self._duration = None
68         self.tx_stats = None
69         self.rx_stats = None
70         self._log_handle = None
71
72         user_home = os.path.expanduser('~')
73         self._log_path = '{}/Xena/Xena2544-2G/Logs/xena2544.log'.format(
74                 user_home)
75
76         # make the folder and log file if they doesn't exist
77         if not os.path.exists(self._log_path):
78             os.makedirs(os.path.dirname(self._log_path))
79
80         # empty the file contents
81         open(self._log_path, 'w').close()
82
83
84     @property
85     def traffic_defaults(self):
86         """Default traffic values.
87
88         These can be expected to be constant across traffic generators,
89         so no setter is provided. Changes to the structure or contents
90         will likely break traffic generator implementations or tests
91         respectively.
92         """
93         return self._traffic_defaults
94
95     @staticmethod
96     def _create_throughput_result(root):
97         """
98         Create the results based off the output xml file from the Xena2544.exe
99         execution
100         :param root: root dictionary from xml import
101         :return: Results Ordered dictionary based off ResultsConstants
102         """
103         # get the test type from the report file
104         test_type = root[0][1].get('TestType')
105         # set the version from the report file
106         settings.setValue('XENA_VERSION', root[0][0][1].get('GeneratedBy'))
107
108         if test_type == 'Throughput':
109             results = OrderedDict()
110             results[ResultsConstants.THROUGHPUT_RX_FPS] = float(
111                 root[0][1][0][0].get('PortRxPps')) + float(
112                     root[0][1][0][1].get('PortRxPps'))
113             results[ResultsConstants.THROUGHPUT_RX_MBPS] = (float(
114                 root[0][1][0][0].get('PortRxBpsL1')) + float(
115                     root[0][1][0][1].get('PortRxBpsL1')))/ 1000000
116             results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
117                 100 - float(root[0][1][0].get('TotalLossRatioPcnt'))) * float(
118                     root[0][1][0].get('TotalTxRatePcnt'))/100
119             results[ResultsConstants.TX_RATE_FPS] = root[0][1][0].get(
120                 'TotalTxRateFps')
121             results[ResultsConstants.TX_RATE_MBPS] = float(
122                 root[0][1][0].get('TotalTxRateBpsL1')) / 1000000
123             results[ResultsConstants.TX_RATE_PERCENT] = root[0][1][0].get(
124                 'TotalTxRatePcnt')
125             try:
126                 results[ResultsConstants.MIN_LATENCY_NS] = float(
127                     root[0][1][0][0].get('MinLatency')) * 1000
128             except ValueError:
129                 # Stats for latency returned as N/A so just post them
130                 results[ResultsConstants.MIN_LATENCY_NS] = root[0][1][0][0].get(
131                     'MinLatency')
132             try:
133                 results[ResultsConstants.MAX_LATENCY_NS] = float(
134                     root[0][1][0][0].get('MaxLatency')) * 1000
135             except ValueError:
136                 # Stats for latency returned as N/A so just post them
137                 results[ResultsConstants.MAX_LATENCY_NS] = root[0][1][0][0].get(
138                     'MaxLatency')
139             try:
140                 results[ResultsConstants.AVG_LATENCY_NS] = float(
141                     root[0][1][0][0].get('AvgLatency')) * 1000
142             except ValueError:
143                 # Stats for latency returned as N/A so just post them
144                 results[ResultsConstants.AVG_LATENCY_NS] = root[0][1][0][0].get(
145                     'AvgLatency')
146         elif test_type == 'Back2Back':
147             results = OrderedDict()
148
149             # Just mimic what Ixia does and only return the b2b frame count.
150             # This may change later once its decided the common results stats
151             # to be returned should be.
152             results[ResultsConstants.B2B_FRAMES] = root[0][1][0][0].get(
153                 'TotalTxBurstFrames')
154         else:
155             raise NotImplementedError('Unknown test type in report file.')
156
157         return results
158
159     def _build_packet_header(self, reverse=False):
160         """
161         Build a packet header based on traffic profile using scapy external
162         libraries.
163         :param reverse: Swap source and destination info when building header
164         :return: packet header in hex
165         """
166         srcmac = self._params['traffic']['l2'][
167             'srcmac'] if not reverse else self._params['traffic']['l2'][
168                 'dstmac']
169         dstmac = self._params['traffic']['l2'][
170             'dstmac'] if not reverse else self._params['traffic']['l2'][
171                 'srcmac']
172         srcip = self._params['traffic']['l3'][
173             'srcip'] if not reverse else self._params['traffic']['l3']['dstip']
174         dstip = self._params['traffic']['l3'][
175             'dstip'] if not reverse else self._params['traffic']['l3']['srcip']
176         layer2 = inet.Ether(src=srcmac, dst=dstmac)
177         layer3 = inet.IP(src=srcip, dst=dstip,
178                          proto=self._params['traffic']['l3']['proto'])
179         layer4 = inet.UDP(sport=self._params['traffic']['l4']['srcport'],
180                           dport=self._params['traffic']['l4']['dstport'])
181         if self._params['traffic']['vlan']['enabled']:
182             vlan = inet.Dot1Q(vlan=self._params['traffic']['vlan']['id'],
183                               prio=self._params['traffic']['vlan']['priority'],
184                               id=self._params['traffic']['vlan']['cfi'])
185         else:
186             vlan = None
187         packet = layer2/vlan/layer3/layer4 if vlan else layer2/layer3/layer4
188         packet_bytes = bytes(packet)
189         packet_hex = '0x' + binascii.hexlify(packet_bytes).decode('utf-8')
190         return packet_hex
191
192     def _create_api_result(self):
193         """
194         Create result dictionary per trafficgen specifications from socket API
195         stats. If stats are not available return values of 0.
196         :return: ResultsConstants as dictionary
197         """
198         # Handle each case of statistics based on if the data is available.
199         # This prevents uncaught exceptions when the stats aren't available.
200         result_dict = OrderedDict()
201         if self.tx_stats.data.get(self.tx_stats.pt_stream_keys[0]):
202             result_dict[ResultsConstants.TX_FRAMES] = self.tx_stats.data[
203                 self.tx_stats.pt_stream_keys[0]]['packets']
204             result_dict[ResultsConstants.TX_RATE_FPS] = self.tx_stats.data[
205                 self.tx_stats.pt_stream_keys[0]]['pps']
206             result_dict[ResultsConstants.TX_RATE_MBPS] = self.tx_stats.data[
207                 self.tx_stats.pt_stream_keys[0]]['bps'] / 1000000
208             result_dict[ResultsConstants.TX_BYTES] = self.tx_stats.data[
209                 self.tx_stats.pt_stream_keys[0]]['bytes']
210             # tx rate percent may need to be halved if bi directional
211             result_dict[ResultsConstants.TX_RATE_PERCENT] = line_percentage(
212                 self.xmanager.ports[0], self.tx_stats, self._duration,
213                 self._params['traffic']['l2']['framesize']) if \
214                 self._params['traffic']['bidir'] == 'False' else\
215                 line_percentage(
216                     self.xmanager.ports[0], self.tx_stats, self._duration,
217                     self._params['traffic']['l2']['framesize']) / 2
218         else:
219             self._logger.error('Transmit stats not available.')
220             result_dict[ResultsConstants.TX_FRAMES] = 0
221             result_dict[ResultsConstants.TX_RATE_FPS] = 0
222             result_dict[ResultsConstants.TX_RATE_MBPS] = 0
223             result_dict[ResultsConstants.TX_BYTES] = 0
224             result_dict[ResultsConstants.TX_RATE_PERCENT] = 0
225
226         if self.rx_stats.data.get('pr_tpldstraffic'):
227             result_dict[ResultsConstants.RX_FRAMES] = self.rx_stats.data[
228                 'pr_tpldstraffic']['0']['packets']
229             result_dict[
230                 ResultsConstants.THROUGHPUT_RX_FPS] = self.rx_stats.data[
231                     'pr_tpldstraffic']['0']['pps']
232             result_dict[
233                 ResultsConstants.THROUGHPUT_RX_MBPS] = self.rx_stats.data[
234                     'pr_tpldstraffic']['0']['bps'] / 1000000
235             result_dict[ResultsConstants.RX_BYTES] = self.rx_stats.data[
236                 'pr_tpldstraffic']['0']['bytes']
237             # throughput percent may need to be halved if bi directional
238             result_dict[
239                 ResultsConstants.THROUGHPUT_RX_PERCENT] = line_percentage(
240                     self.xmanager.ports[1], self.rx_stats, self._duration,
241                     self._params['traffic']['l2']['framesize']) if \
242                 self._params['traffic']['bidir'] == 'False' else \
243                 line_percentage(
244                     self.xmanager.ports[1], self.rx_stats, self._duration,
245                     self._params['traffic']['l2']['framesize']) / 2
246
247         else:
248             self._logger.error('Receive stats not available.')
249             result_dict[ResultsConstants.RX_FRAMES] = 0
250             result_dict[ResultsConstants.THROUGHPUT_RX_FPS] = 0
251             result_dict[ResultsConstants.THROUGHPUT_RX_MBPS] = 0
252             result_dict[ResultsConstants.RX_BYTES] = 0
253             result_dict[ResultsConstants.THROUGHPUT_RX_PERCENT] = 0
254
255         if self.rx_stats.data.get('pr_tplderrors'):
256             result_dict[ResultsConstants.PAYLOAD_ERR] = self.rx_stats.data[
257                 'pr_tplderrors']['0']['pld']
258             result_dict[ResultsConstants.SEQ_ERR] = self.rx_stats.data[
259                 'pr_tplderrors']['0']['seq']
260         else:
261             result_dict[ResultsConstants.PAYLOAD_ERR] = 0
262             result_dict[ResultsConstants.SEQ_ERR] = 0
263
264         if self.rx_stats.data.get('pr_tpldlatency'):
265             result_dict[ResultsConstants.MIN_LATENCY_NS] = self.rx_stats.data[
266                 'pr_tpldlatency']['0']['min']
267             result_dict[ResultsConstants.MAX_LATENCY_NS] = self.rx_stats.data[
268                 'pr_tpldlatency']['0']['max']
269             result_dict[ResultsConstants.AVG_LATENCY_NS] = self.rx_stats.data[
270                 'pr_tpldlatency']['0']['avg']
271         else:
272             result_dict[ResultsConstants.MIN_LATENCY_NS] = 0
273             result_dict[ResultsConstants.MAX_LATENCY_NS] = 0
274             result_dict[ResultsConstants.AVG_LATENCY_NS] = 0
275
276         return result_dict
277
278     def _setup_json_config(self, trials, loss_rate, testtype=None):
279         """
280         Create a 2bUsed json file that will be used for xena2544.exe execution.
281         :param trials: Number of trials
282         :param loss_rate: The acceptable loss rate as float
283         :param testtype: Either '2544_b2b' or '2544_throughput' as string
284         :return: None
285         """
286         try:
287             j_file = XenaJSON('./tools/pkt_gen/xena/profiles/baseconfig.x2544')
288             j_file.set_chassis_info(
289                 settings.getValue('TRAFFICGEN_XENA_IP'),
290                 settings.getValue('TRAFFICGEN_XENA_PASSWORD')
291             )
292             j_file.set_port(0, settings.getValue('TRAFFICGEN_XENA_MODULE1'),
293                             settings.getValue('TRAFFICGEN_XENA_PORT1'))
294             j_file.set_port(1, settings.getValue('TRAFFICGEN_XENA_MODULE2'),
295                             settings.getValue('TRAFFICGEN_XENA_PORT2'))
296             j_file.set_port_ip_v4(
297                 0, settings.getValue("TRAFFICGEN_XENA_PORT0_IP"),
298                 settings.getValue("TRAFFICGEN_XENA_PORT0_CIDR"),
299                 settings.getValue("TRAFFICGEN_XENA_PORT0_GATEWAY"))
300             j_file.set_port_ip_v4(
301                 1, settings.getValue("TRAFFICGEN_XENA_PORT1_IP"),
302                 settings.getValue("TRAFFICGEN_XENA_PORT1_CIDR"),
303                 settings.getValue("TRAFFICGEN_XENA_PORT1_GATEWAY"))
304
305             if testtype == '2544_throughput':
306                 j_file.set_test_options_tput(
307                     packet_sizes=self._params['traffic']['l2']['framesize'],
308                     iterations=trials, loss_rate=loss_rate,
309                     duration=self._duration, micro_tpld=True if self._params[
310                         'traffic']['l2']['framesize'] == 64 else False)
311                 j_file.enable_throughput_test()
312
313             elif testtype == '2544_b2b':
314                 j_file.set_test_options_back2back(
315                     packet_sizes=self._params['traffic']['l2']['framesize'],
316                     iterations=trials, duration=self._duration,
317                     startvalue=self._params['traffic']['frame_rate'],
318                     endvalue=self._params['traffic']['frame_rate'],
319                     micro_tpld=True if self._params[
320                         'traffic']['l2']['framesize'] == 64 else False)
321                 j_file.enable_back2back_test()
322
323             j_file.set_header_layer2(
324                 dst_mac=self._params['traffic']['l2']['dstmac'],
325                 src_mac=self._params['traffic']['l2']['srcmac'])
326             j_file.set_header_layer3(
327                 src_ip=self._params['traffic']['l3']['srcip'],
328                 dst_ip=self._params['traffic']['l3']['dstip'],
329                 protocol=self._params['traffic']['l3']['proto'])
330             j_file.set_header_layer4_udp(
331                 source_port=self._params['traffic']['l4']['srcport'],
332                 destination_port=self._params['traffic']['l4']['dstport'])
333             if self._params['traffic']['vlan']['enabled']:
334                 j_file.set_header_vlan(
335                     vlan_id=self._params['traffic']['vlan']['id'],
336                     id=self._params['traffic']['vlan']['cfi'],
337                     prio=self._params['traffic']['vlan']['priority'])
338             j_file.add_header_segments(
339                 flows=self._params['traffic']['multistream'],
340                 multistream_layer=self._params['traffic']['stream_type'])
341             # set duplex mode
342             if self._params['traffic']['bidir'] == "True":
343                 j_file.set_topology_mesh()
344             else:
345                 j_file.set_topology_blocks()
346
347             j_file.write_config('./tools/pkt_gen/xena/profiles/2bUsed.x2544')
348         except Exception as exc:
349             self._logger.exception("Error during Xena JSON setup: %s", exc)
350             raise
351
352     def _start_traffic_api(self, packet_limit):
353         """
354         Start the Xena traffic using the socket API driver
355         :param packet_limit: packet limit for stream, set to -1 for no limit
356         :return: None
357         """
358         if not self.xmanager:
359             self._xsocket = XenaSocketDriver(
360                 settings.getValue('TRAFFICGEN_XENA_IP'))
361             self.xmanager = XenaManager(
362                 self._xsocket, settings.getValue('TRAFFICGEN_XENA_USER'),
363                 settings.getValue('TRAFFICGEN_XENA_PASSWORD'))
364
365         # for the report file version info ask the chassis directly for its
366         # software versions
367         settings.setValue('XENA_VERSION', 'XENA Socket API - {}'.format(
368             self.xmanager.get_version()))
369
370         if not len(self.xmanager.ports):
371             self.xmanager.ports[0] = self.xmanager.add_module_port(
372                 settings.getValue('TRAFFICGEN_XENA_MODULE1'),
373                 settings.getValue('TRAFFICGEN_XENA_PORT1'))
374             if not self.xmanager.ports[0].reserve_port():
375                 self._logger.error(
376                     'Unable to reserve port 0. Please release Xena Port')
377
378         if len(self.xmanager.ports) < 2:
379             self.xmanager.ports[1] = self.xmanager.add_module_port(
380                 settings.getValue('TRAFFICGEN_XENA_MODULE2'),
381                 settings.getValue('TRAFFICGEN_XENA_PORT2'))
382             if not self.xmanager.ports[1].reserve_port():
383                 self._logger.error(
384                     'Unable to reserve port 1. Please release Xena Port')
385
386         # Clear port configuration for a clean start
387         self.xmanager.ports[0].reset_port()
388         self.xmanager.ports[1].reset_port()
389         self.xmanager.ports[0].clear_stats()
390         self.xmanager.ports[1].clear_stats()
391
392         # set the port IP from the conf file
393         self.xmanager.ports[0].set_port_ip(
394             settings.getValue('TRAFFICGEN_XENA_PORT0_IP'),
395             settings.getValue('TRAFFICGEN_XENA_PORT0_CIDR'),
396             settings.getValue('TRAFFICGEN_XENA_PORT0_GATEWAY'))
397         self.xmanager.ports[1].set_port_ip(
398             settings.getValue('TRAFFICGEN_XENA_PORT1_IP'),
399             settings.getValue('TRAFFICGEN_XENA_PORT1_CIDR'),
400             settings.getValue('TRAFFICGEN_XENA_PORT1_GATEWAY'))
401
402         def setup_stream(stream, port, payload_id, flip_addr=False):
403             """
404             Helper function to configure streams.
405             :param stream: Stream object from XenaDriver module
406             :param port: Port object from XenaDriver module
407             :param payload_id: payload ID as int
408             :param flip_addr: Boolean if the source and destination addresses
409             should be flipped.
410             :return: None
411             """
412             stream.set_on()
413             stream.set_packet_limit(packet_limit)
414
415             stream.set_rate_fraction(
416                 10000 * self._params['traffic']['frame_rate'])
417             stream.set_packet_header(self._build_packet_header(
418                 reverse=flip_addr))
419             stream.set_header_protocol(
420                 'ETHERNET VLAN IP UDP' if self._params['traffic']['vlan'][
421                     'enabled'] else 'ETHERNET IP UDP')
422             stream.set_packet_length(
423                 'fixed', self._params['traffic']['l2']['framesize'], 16383)
424             stream.set_packet_payload('incrementing', '0x00')
425             stream.set_payload_id(payload_id)
426             port.set_port_time_limit(self._duration * 1000000)
427
428             if self._params['traffic']['l2']['framesize'] == 64:
429                 # set micro tpld
430                 port.micro_tpld_enable()
431
432             if self._params['traffic']['multistream']:
433                 stream.enable_multistream(
434                     flows=self._params['traffic']['multistream'],
435                     layer=self._params['traffic']['stream_type'])
436
437         s1_p0 = self.xmanager.ports[0].add_stream()
438         setup_stream(s1_p0, self.xmanager.ports[0], 0)
439
440         if self._params['traffic']['bidir'] == 'True':
441             s1_p1 = self.xmanager.ports[1].add_stream()
442             setup_stream(s1_p1, self.xmanager.ports[1], 1, flip_addr=True)
443
444         if not self.xmanager.ports[0].traffic_on():
445             self._logger.error(
446                 "Failure to start port 0. Check settings and retry.")
447         if self._params['traffic']['bidir'] == 'True':
448             if not self.xmanager.ports[1].traffic_on():
449                 self._logger.error(
450                     "Failure to start port 1. Check settings and retry.")
451         sleep(self._duration)
452         # getting results
453         if self._params['traffic']['bidir'] == 'True':
454             # need to aggregate out both ports stats and assign that data
455             self.rx_stats = self.xmanager.ports[1].get_rx_stats()
456             self.tx_stats = self.xmanager.ports[0].get_tx_stats()
457             self.tx_stats.data = aggregate_stats(
458                 self.tx_stats.data,
459                 self.xmanager.ports[1].get_tx_stats().data)
460             self.rx_stats.data = aggregate_stats(
461                 self.rx_stats.data,
462                 self.xmanager.ports[0].get_rx_stats().data)
463         else:
464             # no need to aggregate, just grab the appropriate port stats
465             self.tx_stats = self.xmanager.ports[0].get_tx_stats()
466             self.rx_stats = self.xmanager.ports[1].get_rx_stats()
467         sleep(1)
468
469     def _start_xena_2544(self):
470         """
471         Start the xena2544 exe.
472         :return: None
473         """
474         args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c",
475                 "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r",
476                 "./tools/pkt_gen/xena", "-u",
477                 settings.getValue('TRAFFICGEN_XENA_USER')]
478         # Sometimes Xena2544.exe completes, but mono holds the process without
479         # releasing it, this can cause a deadlock of the main thread. Use the
480         # xena log file as a way to detect this.
481         self._log_handle = open(self._log_path, 'r')
482         # read the contents of the log before we start so the next read in the
483         # wait method are only looking at the text from this test instance
484         self._log_handle.read()
485         self.mono_pipe = subprocess.Popen(args, stdout=sys.stdout)
486
487     def _wait_xena_2544_complete(self):
488         """
489         Wait for Xena2544.exe completion.
490         :return: None
491         """
492         data = ''
493         while True:
494             try:
495                 self.mono_pipe.wait(60)
496                 self._log_handle.close()
497                 break
498             except subprocess.TimeoutExpired:
499                 # check the log to see if Xena2544 has completed and mono is
500                 # deadlocked.
501                 data += self._log_handle.read()
502                 if 'TestCompletedSuccessfully' in data:
503                     self._log_handle.close()
504                     self.mono_pipe.terminate()
505                     break
506
507     def _stop_api_traffic(self):
508         """
509         Stop traffic through the socket API
510         :return: Return results from _create_api_result method
511         """
512         self.xmanager.ports[0].traffic_off()
513         if self._params['traffic']['bidir'] == 'True':
514             self.xmanager.ports[1].traffic_off()
515         sleep(5)
516
517         stat = self._create_api_result()
518         self.disconnect()
519         return stat
520
521     def connect(self):
522         self._logger.debug('Connect')
523         return self
524
525     def disconnect(self):
526         """Disconnect from the traffic generator.
527
528         As with :func:`connect`, this function is optional.
529
530
531         Where implemented, this function should raise an exception on
532         failure.
533
534         :returns: None
535         """
536         self._logger.debug('disconnect')
537         if self.xmanager:
538             self.xmanager.disconnect()
539             self.xmanager = None
540
541         if self._xsocket:
542             self._xsocket.disconnect()
543             self._xsocket = None
544
545     def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
546         """Send a burst of traffic.
547
548         See ITrafficGenerator for description
549         """
550         self._duration = duration
551         self._params.clear()
552         self._params['traffic'] = self.traffic_defaults.copy()
553         if traffic:
554             self._params['traffic'] = merge_spec(self._params['traffic'],
555                                                  traffic)
556         self._start_traffic_api(numpkts)
557         return self._stop_api_traffic()
558
559     def send_cont_traffic(self, traffic=None, duration=20):
560         """Send a continuous flow of traffic.
561
562         See ITrafficGenerator for description
563         """
564         self._duration = duration
565
566         self._params.clear()
567         self._params['traffic'] = self.traffic_defaults.copy()
568         if traffic:
569             self._params['traffic'] = merge_spec(self._params['traffic'],
570                                                  traffic)
571         self._start_traffic_api(-1)
572         return self._stop_api_traffic()
573
574     def start_cont_traffic(self, traffic=None, duration=20):
575         """Non-blocking version of 'send_cont_traffic'.
576
577         See ITrafficGenerator for description
578         """
579         self._duration = duration
580
581         self._params.clear()
582         self._params['traffic'] = self.traffic_defaults.copy()
583         if traffic:
584             self._params['traffic'] = merge_spec(self._params['traffic'],
585                                                  traffic)
586         self._start_traffic_api(-1)
587
588     def stop_cont_traffic(self):
589         """Stop continuous transmission and return results.
590         """
591         return self._stop_api_traffic()
592
593     def send_rfc2544_throughput(self, traffic=None, trials=3, duration=20,
594                                 lossrate=0.0):
595         """Send traffic per RFC2544 throughput test specifications.
596
597         See ITrafficGenerator for description
598         """
599         self._duration = duration
600
601         self._params.clear()
602         self._params['traffic'] = self.traffic_defaults.copy()
603         if traffic:
604             self._params['traffic'] = merge_spec(self._params['traffic'],
605                                                  traffic)
606         self._setup_json_config(trials, lossrate, '2544_throughput')
607         self._start_xena_2544()
608         self._wait_xena_2544_complete()
609
610         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
611         return Xena._create_throughput_result(root)
612
613     def start_rfc2544_throughput(self, traffic=None, trials=3, duration=20,
614                                  lossrate=0.0):
615         """Non-blocking version of 'send_rfc2544_throughput'.
616
617         See ITrafficGenerator for description
618         """
619         self._duration = duration
620         self._params.clear()
621         self._params['traffic'] = self.traffic_defaults.copy()
622         if traffic:
623             self._params['traffic'] = merge_spec(self._params['traffic'],
624                                                  traffic)
625         self._setup_json_config(trials, lossrate, '2544_throughput')
626         self._start_xena_2544()
627
628     def wait_rfc2544_throughput(self):
629         """Wait for and return results of RFC2544 test.
630
631         See ITrafficGenerator for description
632         """
633         self._wait_xena_2544_complete()
634         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
635         return Xena._create_throughput_result(root)
636
637     def send_rfc2544_back2back(self, traffic=None, trials=1, duration=20,
638                                lossrate=0.0):
639         """Send traffic per RFC2544 back2back test specifications.
640
641         See ITrafficGenerator for description
642         """
643         self._duration = duration
644
645         self._params.clear()
646         self._params['traffic'] = self.traffic_defaults.copy()
647         if traffic:
648             self._params['traffic'] = merge_spec(self._params['traffic'],
649                                                  traffic)
650         self._setup_json_config(trials, lossrate, '2544_b2b')
651         self._start_xena_2544()
652         self._wait_xena_2544_complete()
653         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
654         return Xena._create_throughput_result(root)
655
656     def start_rfc2544_back2back(self, traffic=None, trials=1, duration=20,
657                                 lossrate=0.0):
658         """Non-blocking version of 'send_rfc2544_back2back'.
659
660         See ITrafficGenerator for description
661         """
662         self._duration = duration
663         self._params.clear()
664         self._params['traffic'] = self.traffic_defaults.copy()
665         if traffic:
666             self._params['traffic'] = merge_spec(self._params['traffic'],
667                                                  traffic)
668         self._setup_json_config(trials, lossrate, '2544_b2b')
669         self._start_xena_2544()
670
671     def wait_rfc2544_back2back(self):
672         """Wait and set results of RFC2544 test.
673         """
674         self._wait_xena_2544_complete()
675         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
676         return Xena._create_throughput_result(root)
677
678
679 if __name__ == "__main__":
680     pass
681