trex: Add support for burst traffic type
[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 import xml.etree.ElementTree as ET
32 from collections import OrderedDict
33 from time import sleep
34
35 import scapy.layers.inet as inet
36
37 from conf import merge_spec
38 from conf import settings
39 from core.results.results_constants import ResultsConstants
40 from tools.pkt_gen.trafficgen.trafficgen import ITrafficGenerator
41 from tools.pkt_gen.xena.XenaDriver import (
42     aggregate_stats,
43     line_percentage,
44     XenaSocketDriver,
45     XenaManager,
46     )
47 from tools.pkt_gen.xena.json.xena_json_mesh import XenaJSONMesh
48 from tools.pkt_gen.xena.json.xena_json_blocks import XenaJSONBlocks
49 from tools.pkt_gen.xena.json.xena_json_pairs import XenaJSONPairs
50
51 _CURR_DIR = os.path.dirname(os.path.realpath(__file__))
52
53 class Xena(ITrafficGenerator):
54     """
55     Xena Traffic generator wrapper class
56     """
57     _logger = logging.getLogger(__name__)
58
59     def __init__(self):
60         super().__init__()
61         self.mono_pipe = None
62         self.xmanager = None
63         self._params = {}
64         self._xsocket = None
65         self._duration = None
66         self.tx_stats = None
67         self.rx_stats = None
68         self._log_handle = None
69
70         user_home = os.path.expanduser('~')
71         self._log_path = '{}/Xena/Xena2544-2G/Logs/xena2544.log'.format(
72             user_home)
73
74         # make the folder and log file if they doesn't exist
75         if not os.path.exists(self._log_path):
76             os.makedirs(os.path.dirname(self._log_path))
77
78         # empty the file contents
79         open(self._log_path, 'w').close()
80
81     @staticmethod
82     def _create_throughput_result(root):
83         """
84         Create the results based off the output xml file from the Xena2544.exe
85         execution
86         :param root: root dictionary from xml import
87         :return: Results Ordered dictionary based off ResultsConstants
88         """
89         # get the test type from the report file
90         test_type = root[0][1].get('TestType')
91         # set the version from the report file
92         settings.setValue('XENA_VERSION', root[0][0][1].get('GeneratedBy'))
93
94         if test_type == 'Throughput':
95             results = OrderedDict()
96             results[ResultsConstants.THROUGHPUT_RX_FPS] = float(
97                 root[0][1][0][0].get('PortRxPps')) + float(
98                     root[0][1][0][1].get('PortRxPps'))
99             results[ResultsConstants.THROUGHPUT_RX_MBPS] = (float(
100                 root[0][1][0][0].get('PortRxBpsL1')) + float(
101                     root[0][1][0][1].get('PortRxBpsL1')))/ 1000000
102             results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
103                 100 - float(root[0][1][0].get('TotalLossRatioPcnt'))) * float(
104                     root[0][1][0].get('TotalTxRatePcnt'))/100
105             results[ResultsConstants.TX_RATE_FPS] = root[0][1][0].get(
106                 'TotalTxRateFps')
107             results[ResultsConstants.TX_RATE_MBPS] = float(
108                 root[0][1][0].get('TotalTxRateBpsL1')) / 1000000
109             results[ResultsConstants.TX_RATE_PERCENT] = root[0][1][0].get(
110                 'TotalTxRatePcnt')
111             try:
112                 results[ResultsConstants.MIN_LATENCY_NS] = float(
113                     root[0][1][0][0].get('MinLatency')) * 1000
114             except ValueError:
115                 # Stats for latency returned as N/A so just post them
116                 results[ResultsConstants.MIN_LATENCY_NS] = root[0][1][0][0].get(
117                     'MinLatency')
118             try:
119                 results[ResultsConstants.MAX_LATENCY_NS] = float(
120                     root[0][1][0][0].get('MaxLatency')) * 1000
121             except ValueError:
122                 # Stats for latency returned as N/A so just post them
123                 results[ResultsConstants.MAX_LATENCY_NS] = root[0][1][0][0].get(
124                     'MaxLatency')
125             try:
126                 results[ResultsConstants.AVG_LATENCY_NS] = float(
127                     root[0][1][0][0].get('AvgLatency')) * 1000
128             except ValueError:
129                 # Stats for latency returned as N/A so just post them
130                 results[ResultsConstants.AVG_LATENCY_NS] = root[0][1][0][0].get(
131                     'AvgLatency')
132         elif test_type == 'Back2Back':
133             results = OrderedDict()
134
135             # Just mimic what Ixia does and only return the b2b frame count.
136             # This may change later once its decided the common results stats
137             # to be returned should be.
138             results[ResultsConstants.B2B_FRAMES] = root[0][1][0][0].get(
139                 'TotalTxBurstFrames')
140         else:
141             raise NotImplementedError('Unknown test type in report file.')
142
143         return results
144
145     def _build_packet_header(self, reverse=False):
146         """
147         Build a packet header based on traffic profile using scapy external
148         libraries.
149         :param reverse: Swap source and destination info when building header
150         :return: packet header in hex
151         """
152         srcmac = self._params['traffic']['l2'][
153             'srcmac'] if not reverse else self._params['traffic']['l2'][
154                 'dstmac']
155         dstmac = self._params['traffic']['l2'][
156             'dstmac'] if not reverse else self._params['traffic']['l2'][
157                 'srcmac']
158         srcip = self._params['traffic']['l3'][
159             'srcip'] if not reverse else self._params['traffic']['l3']['dstip']
160         dstip = self._params['traffic']['l3'][
161             'dstip'] if not reverse else self._params['traffic']['l3']['srcip']
162         layer2 = inet.Ether(src=srcmac, dst=dstmac)
163         layer3 = inet.IP(src=srcip, dst=dstip,
164                          proto=self._params['traffic']['l3']['proto'])
165         layer4 = inet.UDP(sport=self._params['traffic']['l4']['srcport'],
166                           dport=self._params['traffic']['l4']['dstport'])
167         if self._params['traffic']['vlan']['enabled']:
168             vlan = inet.Dot1Q(vlan=self._params['traffic']['vlan']['id'],
169                               prio=self._params['traffic']['vlan']['priority'],
170                               id=self._params['traffic']['vlan']['cfi'])
171         else:
172             vlan = None
173         packet = layer2/vlan/layer3/layer4 if vlan else layer2/layer3/layer4
174         packet_bytes = bytes(packet)
175         packet_hex = '0x' + binascii.hexlify(packet_bytes).decode('utf-8')
176         return packet_hex
177
178     def _create_api_result(self):
179         """
180         Create result dictionary per trafficgen specifications from socket API
181         stats. If stats are not available return values of 0.
182         :return: ResultsConstants as dictionary
183         """
184         # Handle each case of statistics based on if the data is available.
185         # This prevents uncaught exceptions when the stats aren't available.
186         result_dict = OrderedDict()
187         if self.tx_stats.data.get(self.tx_stats.pt_stream_keys[0]):
188             result_dict[ResultsConstants.TX_FRAMES] = self.tx_stats.data[
189                 self.tx_stats.pt_stream_keys[0]]['packets']
190             result_dict[ResultsConstants.TX_RATE_FPS] = self.tx_stats.data[
191                 self.tx_stats.pt_stream_keys[0]]['packets'] / self._duration
192             result_dict[ResultsConstants.TX_RATE_MBPS] = ((
193                 self.tx_stats.data[self.tx_stats.pt_stream_keys[0]]['bytes']
194                 * 8) / 1000000) / self._duration
195             result_dict[ResultsConstants.TX_BYTES] = self.tx_stats.data[
196                 self.tx_stats.pt_stream_keys[0]]['bytes']
197             # tx rate percent may need to be halved if bi directional
198             result_dict[ResultsConstants.TX_RATE_PERCENT] = line_percentage(
199                 self.xmanager.ports[0], self.tx_stats, self._duration,
200                 self._params['traffic']['l2']['framesize']) if \
201                 self._params['traffic']['bidir'] == 'False' else\
202                 line_percentage(
203                     self.xmanager.ports[0], self.tx_stats, self._duration,
204                     self._params['traffic']['l2']['framesize']) / 2
205         else:
206             self._logger.error('Transmit stats not available.')
207             result_dict[ResultsConstants.TX_FRAMES] = 0
208             result_dict[ResultsConstants.TX_RATE_FPS] = 0
209             result_dict[ResultsConstants.TX_RATE_MBPS] = 0
210             result_dict[ResultsConstants.TX_BYTES] = 0
211             result_dict[ResultsConstants.TX_RATE_PERCENT] = 0
212
213         if self.rx_stats.data.get('pr_tpldstraffic'):
214             result_dict[ResultsConstants.RX_FRAMES] = self.rx_stats.data[
215                 'pr_tpldstraffic']['0']['packets']
216             result_dict[
217                 ResultsConstants.THROUGHPUT_RX_FPS] = self.rx_stats.data[
218                     'pr_tpldstraffic']['0']['packets'] / self._duration
219             result_dict[
220                 ResultsConstants.THROUGHPUT_RX_MBPS] = ((
221                     self.rx_stats.data['pr_tpldstraffic']['0']['bytes']
222                     *8) / 1000000) / self._duration
223             result_dict[ResultsConstants.RX_BYTES] = self.rx_stats.data[
224                 'pr_tpldstraffic']['0']['bytes']
225             # throughput percent may need to be halved if bi directional
226             result_dict[
227                 ResultsConstants.THROUGHPUT_RX_PERCENT] = line_percentage(
228                     self.xmanager.ports[1], self.rx_stats, self._duration,
229                     self._params['traffic']['l2']['framesize']) if \
230                 self._params['traffic']['bidir'] == 'False' else \
231                 line_percentage(
232                     self.xmanager.ports[1], self.rx_stats, self._duration,
233                     self._params['traffic']['l2']['framesize']) / 2
234
235         else:
236             self._logger.error('Receive stats not available.')
237             result_dict[ResultsConstants.RX_FRAMES] = 0
238             result_dict[ResultsConstants.THROUGHPUT_RX_FPS] = 0
239             result_dict[ResultsConstants.THROUGHPUT_RX_MBPS] = 0
240             result_dict[ResultsConstants.RX_BYTES] = 0
241             result_dict[ResultsConstants.THROUGHPUT_RX_PERCENT] = 0
242
243         if self.rx_stats.data.get('pr_tplderrors'):
244             result_dict[ResultsConstants.PAYLOAD_ERR] = self.rx_stats.data[
245                 'pr_tplderrors']['0']['pld']
246             result_dict[ResultsConstants.SEQ_ERR] = self.rx_stats.data[
247                 'pr_tplderrors']['0']['seq']
248         else:
249             result_dict[ResultsConstants.PAYLOAD_ERR] = 0
250             result_dict[ResultsConstants.SEQ_ERR] = 0
251
252         if self.rx_stats.data.get('pr_tpldlatency'):
253             result_dict[ResultsConstants.MIN_LATENCY_NS] = self.rx_stats.data[
254                 'pr_tpldlatency']['0']['min']
255             result_dict[ResultsConstants.MAX_LATENCY_NS] = self.rx_stats.data[
256                 'pr_tpldlatency']['0']['max']
257             result_dict[ResultsConstants.AVG_LATENCY_NS] = self.rx_stats.data[
258                 'pr_tpldlatency']['0']['avg']
259         else:
260             result_dict[ResultsConstants.MIN_LATENCY_NS] = 0
261             result_dict[ResultsConstants.MAX_LATENCY_NS] = 0
262             result_dict[ResultsConstants.AVG_LATENCY_NS] = 0
263
264         return result_dict
265
266     def _setup_json_config(self, tests, loss_rate, testtype=None,
267                            bonding_test=False):
268         """
269         Create a 2bUsed json file that will be used for xena2544.exe execution.
270         :param tests: Number of tests
271         :param loss_rate: The acceptable loss rate as float
272         :param testtype: Either '2544_b2b' or '2544_throughput' as string
273         :param bonding_test: Specify if the test is a bonding test which will
274         enable the pairs topology
275         :return: None
276         """
277         # set duplex mode, this code is valid, pylint complaining with a
278         # warning that many have complained about online.
279         # pylint: disable=redefined-variable-type
280
281         try:
282             if self._params['traffic']['bidir'] == "True":
283                 j_file = XenaJSONMesh()
284             elif self._params['traffic']['bidir'] == "False":
285                 j_file = XenaJSONBlocks()
286             elif bonding_test:
287                 j_file = XenaJSONPairs()
288
289             j_file.set_chassis_info(
290                 settings.getValue('TRAFFICGEN_XENA_IP'),
291                 settings.getValue('TRAFFICGEN_XENA_PASSWORD')
292             )
293             j_file.set_port(0, settings.getValue('TRAFFICGEN_XENA_MODULE1'),
294                             settings.getValue('TRAFFICGEN_XENA_PORT1'))
295             j_file.set_port(1, settings.getValue('TRAFFICGEN_XENA_MODULE2'),
296                             settings.getValue('TRAFFICGEN_XENA_PORT2'))
297             j_file.set_port_ip_v4(
298                 0, settings.getValue("TRAFFICGEN_XENA_PORT0_IP"),
299                 settings.getValue("TRAFFICGEN_XENA_PORT0_CIDR"),
300                 settings.getValue("TRAFFICGEN_XENA_PORT0_GATEWAY"))
301             j_file.set_port_ip_v4(
302                 1, settings.getValue("TRAFFICGEN_XENA_PORT1_IP"),
303                 settings.getValue("TRAFFICGEN_XENA_PORT1_CIDR"),
304                 settings.getValue("TRAFFICGEN_XENA_PORT1_GATEWAY"))
305
306             if testtype == '2544_throughput':
307                 j_file.set_test_options_tput(
308                     packet_sizes=self._params['traffic']['l2']['framesize'],
309                     iterations=tests, loss_rate=loss_rate,
310                     duration=self._duration, micro_tpld=True if self._params[
311                         'traffic']['l2']['framesize'] == 64 else False)
312                 j_file.enable_throughput_test()
313                 j_file.modify_2544_tput_options(
314                     settings.getValue('TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE'),
315                     settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE'),
316                     settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MAX_VALUE'),
317                     settings.getValue(
318                         'TRAFFICGEN_XENA_2544_TPUT_VALUE_RESOLUTION'),
319                     settings.getValue(
320                         'TRAFFICGEN_XENA_2544_TPUT_USEPASS_THRESHHOLD'),
321                     settings.getValue(
322                         'TRAFFICGEN_XENA_2544_TPUT_PASS_THRESHHOLD')
323                 )
324
325             elif testtype == '2544_b2b':
326                 j_file.set_test_options_back2back(
327                     packet_sizes=self._params['traffic']['l2']['framesize'],
328                     iterations=tests, duration=self._duration,
329                     startvalue=self._params['traffic']['frame_rate'],
330                     endvalue=self._params['traffic']['frame_rate'],
331                     micro_tpld=True if self._params[
332                         'traffic']['l2']['framesize'] == 64 else False)
333                 j_file.enable_back2back_test()
334
335             j_file.set_header_layer2(
336                 dst_mac=self._params['traffic']['l2']['dstmac'],
337                 src_mac=self._params['traffic']['l2']['srcmac'])
338             j_file.set_header_layer3(
339                 src_ip=self._params['traffic']['l3']['srcip'],
340                 dst_ip=self._params['traffic']['l3']['dstip'],
341                 protocol=self._params['traffic']['l3']['proto'])
342             j_file.set_header_layer4_udp(
343                 source_port=self._params['traffic']['l4']['srcport'],
344                 destination_port=self._params['traffic']['l4']['dstport'])
345             if self._params['traffic']['vlan']['enabled']:
346                 j_file.set_header_vlan(
347                     vlan_id=self._params['traffic']['vlan']['id'],
348                     id=self._params['traffic']['vlan']['cfi'],
349                     prio=self._params['traffic']['vlan']['priority'])
350             j_file.add_header_segments(
351                 flows=self._params['traffic']['multistream'],
352                 multistream_layer=self._params['traffic']['stream_type'])
353
354             j_file.write_config(os.path.join(
355                 _CURR_DIR, 'profiles/2bUsed.x2544'))
356         except Exception as exc:
357             self._logger.exception("Error during Xena JSON setup: %s", exc)
358             raise
359
360     def _start_traffic_api(self, packet_limit):
361         """
362         Start the Xena traffic using the socket API driver
363         :param packet_limit: packet limit for stream, set to -1 for no limit
364         :return: None
365         """
366         if not self.xmanager:
367             self._xsocket = XenaSocketDriver(
368                 settings.getValue('TRAFFICGEN_XENA_IP'))
369             self.xmanager = XenaManager(
370                 self._xsocket, settings.getValue('TRAFFICGEN_XENA_USER'),
371                 settings.getValue('TRAFFICGEN_XENA_PASSWORD'))
372
373         # for the report file version info ask the chassis directly for its
374         # software versions
375         settings.setValue('XENA_VERSION', 'XENA Socket API - {}'.format(
376             self.xmanager.get_version()))
377
378         if not self.xmanager.ports:
379             self.xmanager.ports[0] = self.xmanager.add_module_port(
380                 settings.getValue('TRAFFICGEN_XENA_MODULE1'),
381                 settings.getValue('TRAFFICGEN_XENA_PORT1'))
382             if not self.xmanager.ports[0].reserve_port():
383                 self._logger.error(
384                     'Unable to reserve port 0. Please release Xena Port')
385
386         if len(self.xmanager.ports) < 2:
387             self.xmanager.ports[1] = self.xmanager.add_module_port(
388                 settings.getValue('TRAFFICGEN_XENA_MODULE2'),
389                 settings.getValue('TRAFFICGEN_XENA_PORT2'))
390             if not self.xmanager.ports[1].reserve_port():
391                 self._logger.error(
392                     'Unable to reserve port 1. Please release Xena Port')
393
394         # Clear port configuration for a clean start
395         self.xmanager.ports[0].reset_port()
396         self.xmanager.ports[1].reset_port()
397         if settings.getValue('TRAFFICGEN_XENA_CONT_PORT_LEARNING_ENABLED'):
398             # turn on port learning
399             self.xmanager.ports[0].set_port_learning(1)
400             self.xmanager.ports[1].set_port_learning(1)
401             sleep(settings.getValue('TRAFFICGEN_XENA_CONT_PORT_LEARNING_DURATION'))
402             # turn off port learning
403             self.xmanager.ports[0].set_port_learning(0)
404             self.xmanager.ports[1].set_port_learning(0)
405             sleep(1)
406         self.xmanager.ports[0].clear_stats()
407         self.xmanager.ports[1].clear_stats()
408
409         # set the port IP from the conf file
410         self.xmanager.ports[0].set_port_ip(
411             settings.getValue('TRAFFICGEN_XENA_PORT0_IP'),
412             settings.getValue('TRAFFICGEN_XENA_PORT0_CIDR'),
413             settings.getValue('TRAFFICGEN_XENA_PORT0_GATEWAY'))
414         self.xmanager.ports[1].set_port_ip(
415             settings.getValue('TRAFFICGEN_XENA_PORT1_IP'),
416             settings.getValue('TRAFFICGEN_XENA_PORT1_CIDR'),
417             settings.getValue('TRAFFICGEN_XENA_PORT1_GATEWAY'))
418         self.xmanager.ports[0].set_port_time_limit(self._duration)
419         self.xmanager.ports[1].set_port_time_limit(self._duration)
420
421         def setup_stream(stream, port, payload_id, flip_addr=False):
422             """
423             Helper function to configure streams.
424             :param stream: Stream object from XenaDriver module
425             :param port: Port object from XenaDriver module
426             :param payload_id: payload ID as int
427             :param flip_addr: Boolean if the source and destination addresses
428             should be flipped.
429             :return: None
430             """
431             stream.set_on()
432             if packet_limit != -1:
433                 stream.set_packet_limit(packet_limit)
434
435             port.set_port_arp_reply(is_on=True)
436             port.set_port_arp_reply(is_on=True, ipv6=True)
437             port.set_port_ping_reply(is_on=True)
438             port.set_port_ping_reply(is_on=True, ipv6=True)
439
440             stream.set_rate_fraction(int(
441                 10000 * self._params['traffic']['frame_rate']))
442             stream.set_packet_header(self._build_packet_header(
443                 reverse=flip_addr))
444             stream.set_header_protocol(
445                 'ETHERNET VLAN IP UDP' if self._params['traffic']['vlan'][
446                     'enabled'] else 'ETHERNET IP UDP')
447             stream.set_packet_length(
448                 'fixed', self._params['traffic']['l2']['framesize'],
449                 self._params['traffic']['l2']['framesize'])
450             stream.set_packet_payload('incrementing', '0x00')
451             stream.set_payload_id(payload_id)
452             port.set_port_time_limit(self._duration * 1000000)
453
454             if self._params['traffic']['l2']['framesize'] == 64:
455                 # set micro tpld
456                 port.micro_tpld_enable()
457
458             if self._params['traffic']['multistream']:
459                 stream.enable_multistream(
460                     flows=self._params['traffic']['multistream'],
461                     layer=self._params['traffic']['stream_type'])
462
463         s1_p0 = self.xmanager.ports[0].add_stream()
464         setup_stream(s1_p0, self.xmanager.ports[0], 0)
465
466         if self._params['traffic']['bidir'] == 'True':
467             s1_p1 = self.xmanager.ports[1].add_stream()
468             setup_stream(s1_p1, self.xmanager.ports[1], 1, flip_addr=True)
469
470         if not self.xmanager.ports[0].traffic_on():
471             self._logger.error(
472                 "Failure to start port 0. Check settings and retry.")
473         if self._params['traffic']['bidir'] == 'True':
474             if not self.xmanager.ports[1].traffic_on():
475                 self._logger.error(
476                     "Failure to start port 1. Check settings and retry.")
477         sleep(self._duration + 5) # the extra 5 seconds is to allow packets in flight to complete
478         # getting results
479         if self._params['traffic']['bidir'] == 'True':
480             # need to aggregate out both ports stats and assign that data
481             self.rx_stats = self.xmanager.ports[1].get_rx_stats()
482             self.tx_stats = self.xmanager.ports[0].get_tx_stats()
483             self.tx_stats.data = aggregate_stats(
484                 self.tx_stats.data,
485                 self.xmanager.ports[1].get_tx_stats().data)
486             self.rx_stats.data = aggregate_stats(
487                 self.rx_stats.data,
488                 self.xmanager.ports[0].get_rx_stats().data)
489         else:
490             # no need to aggregate, just grab the appropriate port stats
491             self.tx_stats = self.xmanager.ports[0].get_tx_stats()
492             self.rx_stats = self.xmanager.ports[1].get_rx_stats()
493         sleep(1)
494
495     def _start_xena_2544(self):
496         """
497         Start the xena2544 exe.
498         :return: None
499         """
500         args = ["mono", os.path.join(_CURR_DIR, "Xena2544.exe"), "-c",
501                 os.path.join(_CURR_DIR, "profiles/2bUsed.x2544"), "-e", "-r",
502                 _CURR_DIR, "-u",
503                 settings.getValue('TRAFFICGEN_XENA_USER')]
504         # Sometimes Xena2544.exe completes, but mono holds the process without
505         # releasing it, this can cause a deadlock of the main thread. Use the
506         # xena log file as a way to detect this.
507         self._log_handle = open(self._log_path, 'r')
508         # read the contents of the log before we start so the next read in the
509         # wait method are only looking at the text from this test instance
510         self._log_handle.read()
511         self.mono_pipe = subprocess.Popen(args, stdout=sys.stdout)
512
513     def _wait_xena_2544_complete(self):
514         """
515         Wait for Xena2544.exe completion.
516         :return: None
517         """
518         data = ''
519         while True:
520             try:
521                 self.mono_pipe.wait(60)
522                 self._log_handle.close()
523                 break
524             except subprocess.TimeoutExpired:
525                 # check the log to see if Xena2544 has completed and mono is
526                 # deadlocked.
527                 data += self._log_handle.read()
528                 if 'TestCompletedSuccessfully' in data:
529                     self._log_handle.close()
530                     self.mono_pipe.terminate()
531                     break
532
533     def _stop_api_traffic(self):
534         """
535         Stop traffic through the socket API
536         :return: Return results from _create_api_result method
537         """
538         self.xmanager.ports[0].traffic_off()
539         if self._params['traffic']['bidir'] == 'True':
540             self.xmanager.ports[1].traffic_off()
541         sleep(5)
542
543         stat = self._create_api_result()
544         self.disconnect()
545         return stat
546
547     def connect(self):
548         self._logger.debug('Connect')
549         return self
550
551     def disconnect(self):
552         """Disconnect from the traffic generator.
553
554         As with :func:`connect`, this function is optional.
555
556
557         Where implemented, this function should raise an exception on
558         failure.
559
560         :returns: None
561         """
562         self._logger.debug('disconnect')
563         if self.xmanager:
564             self.xmanager.disconnect()
565             self.xmanager = None
566
567         if self._xsocket:
568             self._xsocket.disconnect()
569             self._xsocket = None
570
571     def send_burst_traffic(self, traffic=None, duration=20):
572         """Send a burst of traffic.
573
574         See ITrafficGenerator for description
575         """
576         self._duration = duration
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(traffic['burst_size'])
583         return self._stop_api_traffic()
584
585     def send_cont_traffic(self, traffic=None, duration=20):
586         """Send a continuous flow of 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         return self._stop_api_traffic()
599
600     def start_cont_traffic(self, traffic=None, duration=20):
601         """Non-blocking version of 'send_cont_traffic'.
602
603         See ITrafficGenerator for description
604         """
605         self._duration = duration
606
607         self._params.clear()
608         self._params['traffic'] = self.traffic_defaults.copy()
609         if traffic:
610             self._params['traffic'] = merge_spec(self._params['traffic'],
611                                                  traffic)
612         self._start_traffic_api(-1)
613
614     def stop_cont_traffic(self):
615         """Stop continuous transmission and return results.
616         """
617         return self._stop_api_traffic()
618
619     def send_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
620                                 lossrate=0.0):
621         """Send traffic per RFC2544 throughput test specifications.
622
623         See ITrafficGenerator for description
624         """
625         self._duration = duration
626
627         self._params.clear()
628         self._params['traffic'] = self.traffic_defaults.copy()
629         if traffic:
630             self._params['traffic'] = merge_spec(self._params['traffic'],
631                                                  traffic)
632         self._setup_json_config(tests, lossrate, '2544_throughput')
633         self._start_xena_2544()
634         self._wait_xena_2544_complete()
635
636         root = ET.parse(os.path.join(_CURR_DIR, "xena2544-report.xml")).getroot()
637
638         if settings.getValue('TRAFFICGEN_XENA_RFC2544_VERIFY'):
639             # make sure we have a pass before even trying the verify. No need
640             # to run verify on a failed iteration.
641             root = ET.parse(
642                 os.path.join(_CURR_DIR, "xena2544-report.xml")).getroot()
643             if root[0][1][0].get('TestState') == "FAIL":
644                 self._logger.info('Test failed, skipping verify')
645                 return Xena._create_throughput_result(root)
646
647             # record the previous settings so we can revert to them if needed to
648             # run the binary search again if the verify fails.
649             old_tests = tests
650             old_duration = self._duration
651             old_min = settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE')
652
653             # record the original values to restore after execution
654             orig_min = settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE')
655             orig_max = settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MAX_VALUE')
656             orig_init = settings.getValue('TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE')
657
658             for attempt in range(
659                     1, settings.getValue(
660                         'TRAFFICGEN_XENA_RFC2544_MAXIMUM_VERIFY_ATTEMPTS')+1):
661                 self._logger.info('Running verify attempt %s', attempt)
662                 # get the last pass tx rate from the binary search
663                 pass_rate = float(root[0][1][0].get('TotalTxRatePcnt'))
664                 # run a one pass rfc2544 with the pass rate to see if it passes
665                 # the verify duration
666                 settings.setValue(
667                     'TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE', pass_rate)
668                 settings.setValue(
669                     'TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE', pass_rate)
670                 settings.setValue(
671                     'TRAFFICGEN_XENA_2544_TPUT_MAX_VALUE', pass_rate)
672                 self.start_rfc2544_throughput(
673                     traffic, 1, settings.getValue(
674                         'TRAFFICGEN_XENA_RFC2544_VERIFY_DURATION'), lossrate)
675                 self.wait_rfc2544_throughput()
676                 root = ET.parse(
677                     os.path.join(_CURR_DIR, "xena2544-report.xml")).getroot()
678
679                 # If it passed, report the number of lost frames and exit the
680                 # loop
681                 if root[0][1][0].get('TestState') == "PASS":
682                     self._logger.info('Verify passed, packets lost = %s',
683                                       root[0][1][0].get('TotalLossFrames'))
684                     break
685                 elif attempt < settings.getValue(
686                         'TRAFFICGEN_XENA_RFC2544_MAXIMUM_VERIFY_ATTEMPTS'):
687                     self._logger.info(
688                         'Verify failed, resuming binary search, packets lost = %s',
689                         root[0][1][0].get('TotalLossFrames'))
690                     settings.setValue(
691                         'TRAFFICGEN_XENA_2544_TPUT_MAX_VALUE',
692                         pass_rate - float(settings.getValue(
693                             'TRAFFICGEN_XENA_2544_TPUT_VALUE_RESOLUTION')))
694                     if settings.getValue(
695                             'TRAFFICGEN_XENA_RFC2544_BINARY_RESTART_SMART_SEARCH'):
696                         settings.setValue(
697                             'TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE',
698                             (pass_rate - float(old_min)) / 2)
699                     else:
700                         settings.setValue(
701                             'TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE',
702                             pass_rate - float(settings.getValue(
703                                 'TRAFFICGEN_XENA_2544_TPUT_VALUE_RESOLUTION')))
704                     settings.setValue(
705                         'TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE', old_min)
706                     self._logger.debug(
707                         'RFC2544 Initial rate: %s',
708                         settings.getValue('TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE'))
709                     self._logger.debug(
710                         'RFC2544 Maximum rate: %s',
711                         settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MAX_VALUE'))
712                     self._logger.debug(
713                         'RFC2544 Minimum rate: %s',
714                         settings.getValue('TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE'))
715                     self._duration = old_duration
716                     self.start_rfc2544_throughput(
717                         traffic, old_tests, self._duration, lossrate)
718                     self.wait_rfc2544_throughput()
719                     root = ET.parse(
720                         os.path.join(_CURR_DIR, "xena2544-report.xml")).getroot()
721                 else:
722                     self._logger.error(
723                         'Maximum number of verify attempts reached. Reporting last result')
724
725             #restore original values
726             settings.setValue('TRAFFICGEN_XENA_2544_TPUT_MIN_VALUE', orig_min)
727             settings.setValue('TRAFFICGEN_XENA_2544_TPUT_MAX_VALUE', orig_max)
728             settings.setValue('TRAFFICGEN_XENA_2544_TPUT_INIT_VALUE', orig_init)
729
730         return Xena._create_throughput_result(root)
731
732     def start_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
733                                  lossrate=0.0):
734         """Non-blocking version of 'send_rfc2544_throughput'.
735
736         See ITrafficGenerator for description
737         """
738         self._duration = duration
739         self._params.clear()
740         self._params['traffic'] = self.traffic_defaults.copy()
741         if traffic:
742             self._params['traffic'] = merge_spec(self._params['traffic'],
743                                                  traffic)
744         self._setup_json_config(tests, lossrate, '2544_throughput')
745         self._start_xena_2544()
746
747     def wait_rfc2544_throughput(self):
748         """Wait for and return results of RFC2544 test.
749
750         See ITrafficGenerator for description
751         """
752         self._wait_xena_2544_complete()
753         root = ET.parse(os.path.join(_CURR_DIR, "xena2544-report.xml")).getroot()
754         return Xena._create_throughput_result(root)
755
756     def send_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
757                                lossrate=0.0):
758         """Send traffic per RFC2544 back2back test specifications.
759
760         See ITrafficGenerator for description
761         """
762         self._duration = duration
763
764         self._params.clear()
765         self._params['traffic'] = self.traffic_defaults.copy()
766         if traffic:
767             self._params['traffic'] = merge_spec(self._params['traffic'],
768                                                  traffic)
769         self._setup_json_config(tests, lossrate, '2544_b2b')
770         self._start_xena_2544()
771         self._wait_xena_2544_complete()
772         root = ET.parse(os.path.join(_CURR_DIR, "xena2544-report.xml")).getroot()
773         return Xena._create_throughput_result(root)
774
775     def start_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
776                                 lossrate=0.0):
777         """Non-blocking version of 'send_rfc2544_back2back'.
778
779         See ITrafficGenerator for description
780         """
781         self._duration = duration
782         self._params.clear()
783         self._params['traffic'] = self.traffic_defaults.copy()
784         if traffic:
785             self._params['traffic'] = merge_spec(self._params['traffic'],
786                                                  traffic)
787         self._setup_json_config(tests, lossrate, '2544_b2b')
788         self._start_xena_2544()
789
790     def wait_rfc2544_back2back(self):
791         """Wait and set results of RFC2544 test.
792         """
793         self._wait_xena_2544_complete()
794         root = ET.parse(os.path.join(_CURR_DIR, "xena2544-report.xml")).getroot()
795         return Xena._create_throughput_result(root)
796
797
798 if __name__ == "__main__":
799     pass