Merge "Tools: Improve Stability."
[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, tests, loss_rate, testtype=None):
279         """
280         Create a 2bUsed json file that will be used for xena2544.exe execution.
281         :param tests: Number of tests
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=tests, 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                 j_file.modify_2544_tput_options(
313                     settings.getValue('TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE'),
314                     settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE'),
315                     settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MAX_VALUE'),
316                     settings.getValue(
317                         'TRAFFICGEN_XENA_2544_TPUT_VALUE_RESOLUTION'),
318                     settings.getValue(
319                         'TRAFFICGEN_XENA_2544_TPUT_USEPASS_THRESHHOLD'),
320                     settings.getValue(
321                         'TRAFFICGEN_XENA_2544_TPUT_PASS_THRESHHOLD')
322                 )
323
324             elif testtype == '2544_b2b':
325                 j_file.set_test_options_back2back(
326                     packet_sizes=self._params['traffic']['l2']['framesize'],
327                     iterations=tests, duration=self._duration,
328                     startvalue=self._params['traffic']['frame_rate'],
329                     endvalue=self._params['traffic']['frame_rate'],
330                     micro_tpld=True if self._params[
331                         'traffic']['l2']['framesize'] == 64 else False)
332                 j_file.enable_back2back_test()
333
334             j_file.set_header_layer2(
335                 dst_mac=self._params['traffic']['l2']['dstmac'],
336                 src_mac=self._params['traffic']['l2']['srcmac'])
337             j_file.set_header_layer3(
338                 src_ip=self._params['traffic']['l3']['srcip'],
339                 dst_ip=self._params['traffic']['l3']['dstip'],
340                 protocol=self._params['traffic']['l3']['proto'])
341             j_file.set_header_layer4_udp(
342                 source_port=self._params['traffic']['l4']['srcport'],
343                 destination_port=self._params['traffic']['l4']['dstport'])
344             if self._params['traffic']['vlan']['enabled']:
345                 j_file.set_header_vlan(
346                     vlan_id=self._params['traffic']['vlan']['id'],
347                     id=self._params['traffic']['vlan']['cfi'],
348                     prio=self._params['traffic']['vlan']['priority'])
349             j_file.add_header_segments(
350                 flows=self._params['traffic']['multistream'],
351                 multistream_layer=self._params['traffic']['stream_type'])
352             # set duplex mode
353             if self._params['traffic']['bidir'] == "True":
354                 j_file.set_topology_mesh()
355             else:
356                 j_file.set_topology_blocks()
357
358             j_file.write_config('./tools/pkt_gen/xena/profiles/2bUsed.x2544')
359         except Exception as exc:
360             self._logger.exception("Error during Xena JSON setup: %s", exc)
361             raise
362
363     def _start_traffic_api(self, packet_limit):
364         """
365         Start the Xena traffic using the socket API driver
366         :param packet_limit: packet limit for stream, set to -1 for no limit
367         :return: None
368         """
369         if not self.xmanager:
370             self._xsocket = XenaSocketDriver(
371                 settings.getValue('TRAFFICGEN_XENA_IP'))
372             self.xmanager = XenaManager(
373                 self._xsocket, settings.getValue('TRAFFICGEN_XENA_USER'),
374                 settings.getValue('TRAFFICGEN_XENA_PASSWORD'))
375
376         # for the report file version info ask the chassis directly for its
377         # software versions
378         settings.setValue('XENA_VERSION', 'XENA Socket API - {}'.format(
379             self.xmanager.get_version()))
380
381         if not len(self.xmanager.ports):
382             self.xmanager.ports[0] = self.xmanager.add_module_port(
383                 settings.getValue('TRAFFICGEN_XENA_MODULE1'),
384                 settings.getValue('TRAFFICGEN_XENA_PORT1'))
385             if not self.xmanager.ports[0].reserve_port():
386                 self._logger.error(
387                     'Unable to reserve port 0. Please release Xena Port')
388
389         if len(self.xmanager.ports) < 2:
390             self.xmanager.ports[1] = self.xmanager.add_module_port(
391                 settings.getValue('TRAFFICGEN_XENA_MODULE2'),
392                 settings.getValue('TRAFFICGEN_XENA_PORT2'))
393             if not self.xmanager.ports[1].reserve_port():
394                 self._logger.error(
395                     'Unable to reserve port 1. Please release Xena Port')
396
397         # Clear port configuration for a clean start
398         self.xmanager.ports[0].reset_port()
399         self.xmanager.ports[1].reset_port()
400         self.xmanager.ports[0].clear_stats()
401         self.xmanager.ports[1].clear_stats()
402
403         # set the port IP from the conf file
404         self.xmanager.ports[0].set_port_ip(
405             settings.getValue('TRAFFICGEN_XENA_PORT0_IP'),
406             settings.getValue('TRAFFICGEN_XENA_PORT0_CIDR'),
407             settings.getValue('TRAFFICGEN_XENA_PORT0_GATEWAY'))
408         self.xmanager.ports[1].set_port_ip(
409             settings.getValue('TRAFFICGEN_XENA_PORT1_IP'),
410             settings.getValue('TRAFFICGEN_XENA_PORT1_CIDR'),
411             settings.getValue('TRAFFICGEN_XENA_PORT1_GATEWAY'))
412
413         def setup_stream(stream, port, payload_id, flip_addr=False):
414             """
415             Helper function to configure streams.
416             :param stream: Stream object from XenaDriver module
417             :param port: Port object from XenaDriver module
418             :param payload_id: payload ID as int
419             :param flip_addr: Boolean if the source and destination addresses
420             should be flipped.
421             :return: None
422             """
423             stream.set_on()
424             stream.set_packet_limit(packet_limit)
425
426             stream.set_rate_fraction(
427                 10000 * self._params['traffic']['frame_rate'])
428             stream.set_packet_header(self._build_packet_header(
429                 reverse=flip_addr))
430             stream.set_header_protocol(
431                 'ETHERNET VLAN IP UDP' if self._params['traffic']['vlan'][
432                     'enabled'] else 'ETHERNET IP UDP')
433             stream.set_packet_length(
434                 'fixed', self._params['traffic']['l2']['framesize'], 16383)
435             stream.set_packet_payload('incrementing', '0x00')
436             stream.set_payload_id(payload_id)
437             port.set_port_time_limit(self._duration * 1000000)
438
439             if self._params['traffic']['l2']['framesize'] == 64:
440                 # set micro tpld
441                 port.micro_tpld_enable()
442
443             if self._params['traffic']['multistream']:
444                 stream.enable_multistream(
445                     flows=self._params['traffic']['multistream'],
446                     layer=self._params['traffic']['stream_type'])
447
448         s1_p0 = self.xmanager.ports[0].add_stream()
449         setup_stream(s1_p0, self.xmanager.ports[0], 0)
450
451         if self._params['traffic']['bidir'] == 'True':
452             s1_p1 = self.xmanager.ports[1].add_stream()
453             setup_stream(s1_p1, self.xmanager.ports[1], 1, flip_addr=True)
454
455         if not self.xmanager.ports[0].traffic_on():
456             self._logger.error(
457                 "Failure to start port 0. Check settings and retry.")
458         if self._params['traffic']['bidir'] == 'True':
459             if not self.xmanager.ports[1].traffic_on():
460                 self._logger.error(
461                     "Failure to start port 1. Check settings and retry.")
462         sleep(self._duration)
463         # getting results
464         if self._params['traffic']['bidir'] == 'True':
465             # need to aggregate out both ports stats and assign that data
466             self.rx_stats = self.xmanager.ports[1].get_rx_stats()
467             self.tx_stats = self.xmanager.ports[0].get_tx_stats()
468             self.tx_stats.data = aggregate_stats(
469                 self.tx_stats.data,
470                 self.xmanager.ports[1].get_tx_stats().data)
471             self.rx_stats.data = aggregate_stats(
472                 self.rx_stats.data,
473                 self.xmanager.ports[0].get_rx_stats().data)
474         else:
475             # no need to aggregate, just grab the appropriate port stats
476             self.tx_stats = self.xmanager.ports[0].get_tx_stats()
477             self.rx_stats = self.xmanager.ports[1].get_rx_stats()
478         sleep(1)
479
480     def _start_xena_2544(self):
481         """
482         Start the xena2544 exe.
483         :return: None
484         """
485         args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c",
486                 "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r",
487                 "./tools/pkt_gen/xena", "-u",
488                 settings.getValue('TRAFFICGEN_XENA_USER')]
489         # Sometimes Xena2544.exe completes, but mono holds the process without
490         # releasing it, this can cause a deadlock of the main thread. Use the
491         # xena log file as a way to detect this.
492         self._log_handle = open(self._log_path, 'r')
493         # read the contents of the log before we start so the next read in the
494         # wait method are only looking at the text from this test instance
495         self._log_handle.read()
496         self.mono_pipe = subprocess.Popen(args, stdout=sys.stdout)
497
498     def _wait_xena_2544_complete(self):
499         """
500         Wait for Xena2544.exe completion.
501         :return: None
502         """
503         data = ''
504         while True:
505             try:
506                 self.mono_pipe.wait(60)
507                 self._log_handle.close()
508                 break
509             except subprocess.TimeoutExpired:
510                 # check the log to see if Xena2544 has completed and mono is
511                 # deadlocked.
512                 data += self._log_handle.read()
513                 if 'TestCompletedSuccessfully' in data:
514                     self._log_handle.close()
515                     self.mono_pipe.terminate()
516                     break
517
518     def _stop_api_traffic(self):
519         """
520         Stop traffic through the socket API
521         :return: Return results from _create_api_result method
522         """
523         self.xmanager.ports[0].traffic_off()
524         if self._params['traffic']['bidir'] == 'True':
525             self.xmanager.ports[1].traffic_off()
526         sleep(5)
527
528         stat = self._create_api_result()
529         self.disconnect()
530         return stat
531
532     def connect(self):
533         self._logger.debug('Connect')
534         return self
535
536     def disconnect(self):
537         """Disconnect from the traffic generator.
538
539         As with :func:`connect`, this function is optional.
540
541
542         Where implemented, this function should raise an exception on
543         failure.
544
545         :returns: None
546         """
547         self._logger.debug('disconnect')
548         if self.xmanager:
549             self.xmanager.disconnect()
550             self.xmanager = None
551
552         if self._xsocket:
553             self._xsocket.disconnect()
554             self._xsocket = None
555
556     def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
557         """Send a burst of traffic.
558
559         See ITrafficGenerator for description
560         """
561         self._duration = duration
562         self._params.clear()
563         self._params['traffic'] = self.traffic_defaults.copy()
564         if traffic:
565             self._params['traffic'] = merge_spec(self._params['traffic'],
566                                                  traffic)
567         self._start_traffic_api(numpkts)
568         return self._stop_api_traffic()
569
570     def send_cont_traffic(self, traffic=None, duration=20):
571         """Send a continuous flow of traffic.
572
573         See ITrafficGenerator for description
574         """
575         self._duration = duration
576
577         self._params.clear()
578         self._params['traffic'] = self.traffic_defaults.copy()
579         if traffic:
580             self._params['traffic'] = merge_spec(self._params['traffic'],
581                                                  traffic)
582         self._start_traffic_api(-1)
583         return self._stop_api_traffic()
584
585     def start_cont_traffic(self, traffic=None, duration=20):
586         """Non-blocking version of 'send_cont_traffic'.
587
588         See ITrafficGenerator for description
589         """
590         self._duration = duration
591
592         self._params.clear()
593         self._params['traffic'] = self.traffic_defaults.copy()
594         if traffic:
595             self._params['traffic'] = merge_spec(self._params['traffic'],
596                                                  traffic)
597         self._start_traffic_api(-1)
598
599     def stop_cont_traffic(self):
600         """Stop continuous transmission and return results.
601         """
602         return self._stop_api_traffic()
603
604     def send_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
605                                 lossrate=0.0):
606         """Send traffic per RFC2544 throughput test specifications.
607
608         See ITrafficGenerator for description
609         """
610         self._duration = duration
611
612         self._params.clear()
613         self._params['traffic'] = self.traffic_defaults.copy()
614         if traffic:
615             self._params['traffic'] = merge_spec(self._params['traffic'],
616                                                  traffic)
617         self._setup_json_config(tests, lossrate, '2544_throughput')
618         self._start_xena_2544()
619         self._wait_xena_2544_complete()
620
621         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
622         return Xena._create_throughput_result(root)
623
624     def start_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
625                                  lossrate=0.0):
626         """Non-blocking version of 'send_rfc2544_throughput'.
627
628         See ITrafficGenerator for description
629         """
630         self._duration = duration
631         self._params.clear()
632         self._params['traffic'] = self.traffic_defaults.copy()
633         if traffic:
634             self._params['traffic'] = merge_spec(self._params['traffic'],
635                                                  traffic)
636         self._setup_json_config(tests, lossrate, '2544_throughput')
637         self._start_xena_2544()
638
639     def wait_rfc2544_throughput(self):
640         """Wait for and return results of RFC2544 test.
641
642         See ITrafficGenerator for description
643         """
644         self._wait_xena_2544_complete()
645         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
646         return Xena._create_throughput_result(root)
647
648     def send_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
649                                lossrate=0.0):
650         """Send traffic per RFC2544 back2back test specifications.
651
652         See ITrafficGenerator for description
653         """
654         self._duration = duration
655
656         self._params.clear()
657         self._params['traffic'] = self.traffic_defaults.copy()
658         if traffic:
659             self._params['traffic'] = merge_spec(self._params['traffic'],
660                                                  traffic)
661         self._setup_json_config(tests, lossrate, '2544_b2b')
662         self._start_xena_2544()
663         self._wait_xena_2544_complete()
664         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
665         return Xena._create_throughput_result(root)
666
667     def start_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
668                                 lossrate=0.0):
669         """Non-blocking version of 'send_rfc2544_back2back'.
670
671         See ITrafficGenerator for description
672         """
673         self._duration = duration
674         self._params.clear()
675         self._params['traffic'] = self.traffic_defaults.copy()
676         if traffic:
677             self._params['traffic'] = merge_spec(self._params['traffic'],
678                                                  traffic)
679         self._setup_json_config(tests, lossrate, '2544_b2b')
680         self._start_xena_2544()
681
682     def wait_rfc2544_back2back(self):
683         """Wait and set results of RFC2544 test.
684         """
685         self._wait_xena_2544_complete()
686         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
687         return Xena._create_throughput_result(root)
688
689
690 if __name__ == "__main__":
691     pass
692