xena_version: Add versioning info for report file from Xena
[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 logging
27 import subprocess
28 import sys
29 from time import sleep
30 import xml.etree.ElementTree as ET
31 from collections import OrderedDict
32
33 # VSPerf imports
34 from conf import settings
35 from core.results.results_constants import ResultsConstants
36 from tools.pkt_gen.trafficgen.trafficgenhelper import (
37     TRAFFIC_DEFAULTS,
38     merge_spec)
39 from tools.pkt_gen.trafficgen.trafficgen import ITrafficGenerator
40
41 # Xena module imports
42 from tools.pkt_gen.xena.xena_json import XenaJSON
43
44
45 class Xena(ITrafficGenerator):
46     """
47     Xena Traffic generator wrapper class
48     """
49     _traffic_defaults = TRAFFIC_DEFAULTS.copy()
50     _logger = logging.getLogger(__name__)
51
52     def __init__(self):
53         self.mono_pipe = None
54         self._params = {}
55         self._duration = None
56
57     @property
58     def traffic_defaults(self):
59         """Default traffic values.
60
61         These can be expected to be constant across traffic generators,
62         so no setter is provided. Changes to the structure or contents
63         will likely break traffic generator implementations or tests
64         respectively.
65         """
66         return self._traffic_defaults
67
68     @staticmethod
69     def _create_throughput_result(root):
70         """
71         Create the results based off the output xml file from the Xena2544.exe
72         execution
73         :param root: root dictionary from xml import
74         :return: Results Ordered dictionary based off ResultsConstants
75         """
76         # get the test type from the report file
77         test_type = root[0][1].get('TestType')
78         # set the version from the report file
79         settings.setValue('XENA_VERSION', root[0][0][1].get('GeneratedBy'))
80
81         if test_type == 'Throughput':
82             results = OrderedDict()
83             results[ResultsConstants.THROUGHPUT_RX_FPS] = int(
84                 root[0][1][0][1].get('PortRxPps'))
85             results[ResultsConstants.THROUGHPUT_RX_MBPS] = int(
86                 root[0][1][0][1].get('PortRxBpsL1')) / 1000000
87             results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
88                 100 - int(root[0][1][0].get('TotalLossRatioPcnt'))) * float(
89                     root[0][1][0].get('TotalTxRatePcnt'))/100
90             results[ResultsConstants.TX_RATE_FPS] = root[0][1][0].get(
91                 'TotalTxRateFps')
92             results[ResultsConstants.TX_RATE_MBPS] = float(
93                 root[0][1][0].get('TotalTxRateBpsL1')) / 1000000
94             results[ResultsConstants.TX_RATE_PERCENT] = root[0][1][0].get(
95                 'TotalTxRatePcnt')
96             try:
97                 results[ResultsConstants.MIN_LATENCY_NS] = float(
98                     root[0][1][0][0].get('MinLatency')) * 1000
99             except ValueError:
100                 # Stats for latency returned as N/A so just post them
101                 results[ResultsConstants.MIN_LATENCY_NS] = root[0][1][0][0].get(
102                     'MinLatency')
103             try:
104                 results[ResultsConstants.MAX_LATENCY_NS] = float(
105                     root[0][1][0][0].get('MaxLatency')) * 1000
106             except ValueError:
107                 # Stats for latency returned as N/A so just post them
108                 results[ResultsConstants.MAX_LATENCY_NS] = root[0][1][0][0].get(
109                     'MaxLatency')
110             try:
111                 results[ResultsConstants.AVG_LATENCY_NS] = float(
112                     root[0][1][0][0].get('AvgLatency')) * 1000
113             except ValueError:
114                 # Stats for latency returned as N/A so just post them
115                 results[ResultsConstants.AVG_LATENCY_NS] = root[0][1][0][0].get(
116                     'AvgLatency')
117         elif test_type == 'Back2Back':
118             results = OrderedDict()
119
120             # Just mimic what Ixia does and only return the b2b frame count.
121             # This may change later once its decided the common results stats
122             # to be returned should be.
123             results[ResultsConstants.B2B_FRAMES] = root[0][1][0][0].get(
124                 'TotalTxBurstFrames')
125         else:
126             raise NotImplementedError('Unknown test type in report file.')
127
128         return results
129
130     def _setup_json_config(self, trials, loss_rate, testtype=None):
131         """
132         Create a 2bUsed json file that will be used for xena2544.exe execution.
133         :param trials: Number of trials
134         :param loss_rate: The acceptable loss rate as float
135         :param testtype: Either '2544_b2b' or '2544_throughput' as string
136         :return: None
137         """
138         try:
139             j_file = XenaJSON('./tools/pkt_gen/xena/profiles/baseconfig.x2544')
140             j_file.set_chassis_info(
141                 settings.getValue('TRAFFICGEN_XENA_IP'),
142                 settings.getValue('TRAFFICGEN_XENA_PASSWORD')
143             )
144             j_file.set_port(0, settings.getValue('TRAFFICGEN_XENA_MODULE1'),
145                             settings.getValue('TRAFFICGEN_XENA_PORT1')
146                             )
147             j_file.set_port(1, settings.getValue('TRAFFICGEN_XENA_MODULE2'),
148                             settings.getValue('TRAFFICGEN_XENA_PORT2')
149                             )
150             j_file.set_test_options(
151                 packet_sizes=self._params['traffic']['l2']['framesize'],
152                 iterations=trials, loss_rate=loss_rate,
153                 duration=self._duration, micro_tpld=True if self._params[
154                     'traffic']['l2']['framesize'] == 64 else False)
155             if testtype == '2544_throughput':
156                 j_file.enable_throughput_test()
157             elif testtype == '2544_b2b':
158                 j_file.enable_back2back_test()
159
160             j_file.set_header_layer2(
161                 dst_mac=self._params['traffic']['l2']['dstmac'],
162                 src_mac=self._params['traffic']['l2']['srcmac'])
163             j_file.set_header_layer3(
164                 src_ip=self._params['traffic']['l3']['srcip'],
165                 dst_ip=self._params['traffic']['l3']['dstip'],
166                 protocol=self._params['traffic']['l3']['proto'])
167             j_file.set_header_layer4_udp(
168                 source_port=self._params['traffic']['l4']['srcport'],
169                 destination_port=self._params['traffic']['l4']['dstport'])
170             if self._params['traffic']['vlan']['enabled']:
171                 j_file.set_header_vlan(
172                     vlan_id=self._params['traffic']['vlan']['id'],
173                     id=self._params['traffic']['vlan']['cfi'],
174                     prio=self._params['traffic']['vlan']['priority'])
175             j_file.add_header_segments(
176                 flows=self._params['traffic']['multistream'],
177                 multistream_layer=self._params['traffic']['stream_type'])
178             # set duplex mode
179             if bool(self._params['traffic']['bidir']):
180                 j_file.set_topology_mesh()
181             else:
182                 j_file.set_topology_blocks()
183
184             j_file.write_config('./tools/pkt_gen/xena/profiles/2bUsed.x2544')
185         except Exception as exc:
186             self._logger.exception("Error during Xena JSON setup: %s", exc)
187             raise
188
189     def connect(self):
190         """Connect to the traffic generator.
191
192         This is an optional function, designed for traffic generators
193         which must be "connected to" (i.e. via SSH or an API) before
194         they can be used. If not required, simply do nothing here.
195
196         Where implemented, this function should raise an exception on
197         failure.
198
199         :returns: None
200         """
201         pass
202
203     def disconnect(self):
204         """Disconnect from the traffic generator.
205
206         As with :func:`connect`, this function is optional.
207
208         Where implemented, this function should raise an exception on
209         failure.
210
211         :returns: None
212         """
213         pass
214
215     def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
216         """Send a burst of traffic.
217
218         Send a ``numpkts`` packets of traffic, using ``traffic``
219         configuration, with a timeout of ``time``.
220
221         Attributes:
222         :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
223         :param numpkts: Number of packets to send
224         :param duration: Time to wait to receive packets
225
226         :returns: dictionary of strings with following data:
227             - List of Tx Frames,
228             - List of Rx Frames,
229             - List of Tx Bytes,
230             - List of List of Rx Bytes,
231             - Payload Errors and Sequence Errors.
232         """
233         raise NotImplementedError('Xena burst traffic not implemented')
234
235     def send_cont_traffic(self, traffic=None, duration=20):
236         """Send a continuous flow of traffic.r
237
238         Send packets at ``framerate``, using ``traffic`` configuration,
239         until timeout ``time`` occurs.
240
241         :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
242         :param duration: Time to wait to receive packets (secs)
243         :returns: dictionary of strings with following data:
244             - Tx Throughput (fps),
245             - Rx Throughput (fps),
246             - Tx Throughput (mbps),
247             - Rx Throughput (mbps),
248             - Tx Throughput (% linerate),
249             - Rx Throughput (% linerate),
250             - Min Latency (ns),
251             - Max Latency (ns),
252             - Avg Latency (ns)
253         """
254         raise NotImplementedError('Xena continuous traffic not implemented')
255
256     def start_cont_traffic(self, traffic=None, duration=20):
257         """Non-blocking version of 'send_cont_traffic'.
258
259         Start transmission and immediately return. Do not wait for
260         results.
261         :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
262         :param duration: Time to wait to receive packets (secs)
263         """
264         raise NotImplementedError('Xena continuous traffic not implemented')
265
266     def stop_cont_traffic(self):
267         """Stop continuous transmission and return results.
268         """
269         raise NotImplementedError('Xena continuous traffic not implemented')
270
271     def send_rfc2544_throughput(self, traffic=None, trials=3, duration=20,
272                                 lossrate=0.0):
273         """Send traffic per RFC2544 throughput test specifications.
274
275         See ITrafficGenerator for description
276         """
277         self._duration = duration
278
279         self._params.clear()
280         self._params['traffic'] = self.traffic_defaults.copy()
281         if traffic:
282             self._params['traffic'] = merge_spec(self._params['traffic'],
283                                                  traffic)
284
285         self._setup_json_config(trials, lossrate, '2544_throughput')
286
287         args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c",
288                 "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r",
289                 "./tools/pkt_gen/xena", "-u",
290                 settings.getValue('TRAFFICGEN_XENA_USER')]
291         self.mono_pipe = subprocess.Popen(args, stdout=sys.stdout)
292         self.mono_pipe.communicate()
293         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
294         return Xena._create_throughput_result(root)
295
296     def start_rfc2544_throughput(self, traffic=None, trials=3, duration=20,
297                                  lossrate=0.0):
298         """Non-blocking version of 'send_rfc2544_throughput'.
299
300         See ITrafficGenerator for description
301         """
302         self._duration = duration
303         self._params.clear()
304         self._params['traffic'] = self.traffic_defaults.copy()
305         if traffic:
306             self._params['traffic'] = merge_spec(self._params['traffic'],
307                                                  traffic)
308
309         self._setup_json_config(trials, lossrate, '2544_throughput')
310
311         args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c",
312                 "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r",
313                 "./tools/pkt_gen/xena", "-u",
314                 settings.getValue('TRAFFICGEN_XENA_USER')]
315         self.mono_pipe = subprocess.Popen(args, stdout=sys.stdout)
316
317     def wait_rfc2544_throughput(self):
318         """Wait for and return results of RFC2544 test.
319
320         See ITrafficGenerator for description
321         """
322         self.mono_pipe.communicate()
323         sleep(2)
324         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
325         return Xena._create_throughput_result(root)
326
327     def send_rfc2544_back2back(self, traffic=None, trials=1, duration=20,
328                                lossrate=0.0):
329         """Send traffic per RFC2544 back2back test specifications.
330
331         See ITrafficGenerator for description
332         """
333         self._duration = duration
334
335         self._params.clear()
336         self._params['traffic'] = self.traffic_defaults.copy()
337         if traffic:
338             self._params['traffic'] = merge_spec(self._params['traffic'],
339                                                  traffic)
340
341         self._setup_json_config(trials, lossrate, '2544_b2b')
342
343         args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c",
344                 "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r",
345                 "./tools/pkt_gen/xena", "-u",
346                 settings.getValue('TRAFFICGEN_XENA_USER')]
347         self.mono_pipe = subprocess.Popen(
348             args, stdout=sys.stdout)
349         self.mono_pipe.communicate()
350         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
351         return Xena._create_throughput_result(root)
352
353     def start_rfc2544_back2back(self, traffic=None, trials=1, duration=20,
354                                 lossrate=0.0):
355         """Non-blocking version of 'send_rfc2544_back2back'.
356
357         See ITrafficGenerator for description
358         """
359         self._duration = duration
360
361         self._params.clear()
362         self._params['traffic'] = self.traffic_defaults.copy()
363         if traffic:
364             self._params['traffic'] = merge_spec(self._params['traffic'],
365                                                  traffic)
366
367         self._setup_json_config(trials, lossrate, '2544_b2b')
368
369         args = ["mono", "./tools/pkt_gen/xena/Xena2544.exe", "-c",
370                 "./tools/pkt_gen/xena/profiles/2bUsed.x2544", "-e", "-r",
371                 "./tools/pkt_gen/xena", "-u",
372                 settings.getValue('TRAFFICGEN_XENA_USER')]
373         self.mono_pipe = subprocess.Popen(
374             args, stdout=sys.stdout)
375
376     def wait_rfc2544_back2back(self):
377         """Wait and set results of RFC2544 test.
378         """
379         self.mono_pipe.communicate()
380         sleep(2)
381         root = ET.parse(r'./tools/pkt_gen/xena/xena2544-report.xml').getroot()
382         return Xena._create_throughput_result(root)
383
384
385 if __name__ == "__main__":
386     pass
387