Merge "pkt_gen: MoonGen should use class object for line rate calc"
[vswitchperf.git] / tools / pkt_gen / moongen / moongen.py
1 # Copyright 2016 Red Hat Inc
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 #   Bill Michalowski, Red Hat Inc.
17 #   Andrew Theurer, Red Hat Inc.
18 """
19 Moongen Traffic Generator Model
20 """
21
22 # python imports
23 from collections import OrderedDict
24 import logging
25 import math
26 import re
27 import subprocess
28
29 # VSPerf imports
30 from conf import settings
31 from core.results.results_constants import ResultsConstants
32 from tools.pkt_gen.trafficgen.trafficgenhelper import (
33     TRAFFIC_DEFAULTS,
34     merge_spec)
35 from tools.pkt_gen.trafficgen.trafficgen import ITrafficGenerator
36
37 class Moongen(ITrafficGenerator):
38     """Moongen Traffic generator wrapper."""
39     _traffic_defaults = TRAFFIC_DEFAULTS.copy()
40     _logger = logging.getLogger(__name__)
41
42     def __init__(self):
43         """Moongen class constructor."""
44         self._logger.info("In moongen __init__ method")
45         self._params = {}
46         self._moongen_host_ip_addr = (
47             settings.getValue('TRAFFICGEN_MOONGEN_HOST_IP_ADDR'))
48         self._moongen_base_dir = (
49             settings.getValue('TRAFFICGEN_MOONGEN_BASE_DIR'))
50         self._moongen_user = settings.getValue('TRAFFICGEN_MOONGEN_USER')
51         self._moongen_ports = settings.getValue('TRAFFICGEN_MOONGEN_PORTS')
52
53         if settings.getValue('TRAFFICGEN_MOONGEN_LINE_SPEED_GBPS') == '10':
54             self._moongen_line_speed = math.pow(10, 10)
55         else:
56             raise RuntimeError(
57                 'MOONGEN: Invalid line speed in configuration ' + \
58                 'file (today 10Gbps supported)')
59
60     @property
61     def traffic_defaults(self):
62         """Default traffic values.
63
64         These can be expected to be constant across traffic generators,
65         so no setter is provided. Changes to the structure or contents
66         will likely break traffic generator implementations or tests
67         respectively.
68         """
69         self._logger.info("In Moongen traffic_defaults method")
70         return self._traffic_defaults
71
72     def create_moongen_cfg_file(self, traffic, duration=60,
73                                 acceptable_loss_pct=1, one_shot=0):
74         """Create the Moongen configuration file from VSPERF's traffic profile
75         :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
76         :param duration: The length of time to generate packet throughput
77         :param acceptable_loss: Maximum packet loss acceptable
78         :param one_shot: No RFC 2544 binary search,
79                         just packet flow at traffic specifics
80         """
81         logging.debug("traffic['frame_rate'] = " + \
82             str(traffic['frame_rate']))
83
84         logging.debug("traffic['multistream'] = " + \
85             str(traffic['multistream']))
86
87         logging.debug("traffic['stream_type'] = " + \
88             str(traffic['stream_type']))
89
90         logging.debug("traffic['l2']['srcmac'] = " + \
91             str(traffic['l2']['srcmac']))
92
93         logging.debug("traffic['l2']['dstmac'] = " + \
94             str(traffic['l2']['dstmac']))
95
96         logging.debug("traffic['l3']['proto'] = " + \
97             str(traffic['l3']['proto']))
98
99         logging.debug("traffic['l3']['srcip'] = " + \
100             str(traffic['l3']['srcip']))
101
102         logging.debug("traffic['l3']['dstip'] = " + \
103             str(traffic['l3']['dstip']))
104
105         logging.debug("traffic['l4']['srcport'] = " + \
106             str(traffic['l4']['srcport']))
107
108         logging.debug("traffic['l4']['dstport'] = " + \
109             str(traffic['l4']['dstport']))
110
111         logging.debug("traffic['vlan']['enabled'] = " + \
112             str(traffic['vlan']['enabled']))
113
114         logging.debug("traffic['vlan']['id'] = " + \
115             str(traffic['vlan']['id']))
116
117         logging.debug("traffic['vlan']['priority'] = " + \
118             str(traffic['vlan']['priority']))
119
120         logging.debug("traffic['vlan']['cfi'] = " + \
121             str(traffic['vlan']['cfi']))
122
123         logging.debug(traffic['l2']['framesize'])
124
125         out_file = open("opnfv-vsperf-cfg.lua", "wt")
126
127         out_file.write("VSPERF {\n")
128
129         out_file.write("testType = \"throughput\",\n")
130
131         out_file.write("runBidirec = " + \
132             traffic['bidir'].lower() + ",\n")
133
134         out_file.write("frameSize = " + \
135             str(traffic['l2']['framesize']) + ",\n")
136
137         out_file.write("srcMac = \"" + \
138             str(traffic['l2']['srcmac']) + "\",\n")
139
140         out_file.write("dstMac = \"" + \
141             str(traffic['l2']['dstmac']) + "\",\n")
142
143         out_file.write("srcIp = \"" + \
144             str(traffic['l3']['srcip']) + "\",\n")
145
146         out_file.write("dstIp = \"" + \
147             str(traffic['l3']['dstip']) + "\",\n")
148
149         if traffic['vlan']['enabled']:
150             out_file.write("vlanId = " + \
151                 str(traffic['vlan']['id']) + ",\n")
152
153         out_file.write("searchRunTime = " + \
154             str(duration) + ",\n")
155
156         out_file.write("validationRunTime = " + \
157             str(duration) + ",\n")
158
159         out_file.write("acceptableLossPct = " + \
160             str(acceptable_loss_pct) + ",\n")
161
162         out_file.write("ports = " +\
163             str(self._moongen_ports) +  ",\n")
164
165         if one_shot:
166             out_file.write("oneShot = true,\n")
167
168         # Need to convert VSPERF frame_rate (percentage of line rate)
169         # to Mpps for Moongen
170         start_rate = str(
171             (traffic['frame_rate'] / 100) * (self._moongen_line_speed / \
172             (8 * (traffic['l2']['framesize'] + 20)) / math.pow(10, 6)))
173
174         logging.debug("startRate = " + start_rate)
175
176         out_file.write("startRate = " + \
177             start_rate + "\n")
178
179         out_file.write("}" + "\n")
180         out_file.close()
181
182         copy_moongen_cfg = "scp opnfv-vsperf-cfg.lua " + \
183                             self._moongen_user + "@" + \
184                             self._moongen_host_ip_addr + ":" + \
185                             self._moongen_base_dir + \
186                             "/. && rm opnfv-vsperf-cfg.lua"
187
188         find_moongen = subprocess.Popen(copy_moongen_cfg,
189                                         shell=True,
190                                         stderr=subprocess.PIPE)
191
192         output, error = find_moongen.communicate()
193
194         if error:
195             logging.error(output)
196             logging.error(error)
197             raise RuntimeError('MOONGEN: Error copying configuration file')
198
199     def connect(self):
200         """Connect to Moongen traffic generator
201
202         Verify that Moongen is on the system indicated by
203         the configuration file
204         """
205         self._logger.info("MOONGEN:  In Moongen connect method...")
206
207         if self._moongen_host_ip_addr:
208             cmd_ping = "ping -c1 " + self._moongen_host_ip_addr
209         else:
210             raise RuntimeError('MOONGEN: Moongen host not defined')
211
212         ping = subprocess.Popen(cmd_ping, shell=True, stderr=subprocess.PIPE)
213         output, error = ping.communicate()
214
215         if ping.returncode:
216             self._logger.error(error)
217             self._logger.error(output)
218             raise RuntimeError('MOONGEN: Cannot ping Moongen host at ' + \
219                                self._moongen_host_ip_addr)
220
221         connect_moongen = "ssh " + self._moongen_user + \
222                           "@" + self._moongen_host_ip_addr
223
224         cmd_find_moongen = connect_moongen + " ls " + \
225                            self._moongen_base_dir + "/examples/opnfv-vsperf.lua"
226
227         find_moongen = subprocess.Popen(cmd_find_moongen,
228                                         shell=True,
229                                         stderr=subprocess.PIPE)
230
231         output, error = find_moongen.communicate()
232
233         if find_moongen.returncode:
234             self._logger.error(error)
235             self._logger.error(output)
236             raise RuntimeError(
237                 'MOONGEN: Cannot locate Moongen program at %s within %s' \
238                 % (self._moongen_host_ip_addr, self._moongen_base_dir))
239
240         self._logger.info("MOONGEN: Moongen host successfully found...")
241
242     def disconnect(self):
243         """Disconnect from the traffic generator.
244
245         As with :func:`connect`, this function is optional.
246
247         Where implemented, this function should raise an exception on
248         failure.
249
250         :returns: None
251         """
252         self._logger.info("MOONGEN: In moongen disconnect method")
253
254     def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
255         """Send a burst of traffic.
256
257         Send a ``numpkts`` packets of traffic, using ``traffic``
258         configuration, with a timeout of ``time``.
259
260         :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
261         :param numpkts: Number of packets to send
262         :param duration: Time to wait to receive packets
263
264         :returns: dictionary of strings with following data:
265             - List of Tx Frames,
266             - List of Rx Frames,
267             - List of Tx Bytes,
268             - List of List of Rx Bytes,
269             - Payload Errors and Sequence Errors.
270         """
271         self._logger.info("In Moongen send_burst_traffic method")
272         return NotImplementedError('Moongen Burst traffic not implemented')
273
274     def send_cont_traffic(self, traffic=None, duration=20):
275         """Send a continuous flow of traffic
276
277         Send packets at ``frame rate``, using ``traffic`` configuration,
278         until timeout ``time`` occurs.
279
280         :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
281         :param duration: Time to wait to receive packets (secs)
282         :returns: dictionary of strings with following data:
283             - Tx Throughput (fps),
284             - Rx Throughput (fps),
285             - Tx Throughput (mbps),
286             - Rx Throughput (mbps),
287             - Tx Throughput (% linerate),
288             - Rx Throughput (% linerate),
289             - Min Latency (ns),
290             - Max Latency (ns),
291             - Avg Latency (ns)
292         """
293         self._logger.info("In Moongen send_cont_traffic method")
294
295         self._params.clear()
296         self._params['traffic'] = self.traffic_defaults.copy()
297
298         if traffic:
299             self._params['traffic'] = merge_spec(self._params['traffic'],
300                                                  traffic)
301
302         Moongen.create_moongen_cfg_file(self,
303                                         traffic,
304                                         duration=duration,
305                                         acceptable_loss_pct=100.0,
306                                         one_shot=1)
307
308         collected_results = Moongen.run_moongen_and_collect_results(self,
309                                                                     test_run=1)
310
311         total_throughput_rx_fps = (
312             float(collected_results[ResultsConstants.THROUGHPUT_RX_FPS]))
313
314         total_throughput_rx_mbps = (
315             float(collected_results[ResultsConstants.THROUGHPUT_RX_MBPS]))
316
317         total_throughput_rx_pct = (
318             float(collected_results[ResultsConstants.THROUGHPUT_RX_PERCENT]))
319
320         total_throughput_tx_fps = (
321             float(collected_results[ResultsConstants.TX_RATE_FPS]))
322
323         total_throughput_tx_mbps = (
324             float(collected_results[ResultsConstants.TX_RATE_MBPS]))
325
326         total_throughput_tx_pct = (
327             float(collected_results[ResultsConstants.TX_RATE_PERCENT]))
328
329         total_min_latency_ns = 0
330         total_max_latency_ns = 0
331         total_avg_latency_ns = 0
332
333         results = OrderedDict()
334         results[ResultsConstants.THROUGHPUT_RX_FPS] = (
335             '{:.6f}'.format(total_throughput_rx_fps))
336
337         results[ResultsConstants.THROUGHPUT_RX_MBPS] = (
338             '{:.3f}'.format(total_throughput_rx_mbps))
339
340         results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
341             '{:.3f}'.format(total_throughput_rx_pct))
342
343         results[ResultsConstants.TX_RATE_FPS] = (
344             '{:.6f}'.format(total_throughput_tx_fps))
345
346         results[ResultsConstants.TX_RATE_MBPS] = (
347             '{:.3f}'.format(total_throughput_tx_mbps))
348
349         results[ResultsConstants.TX_RATE_PERCENT] = (
350             '{:.3f}'.format(total_throughput_tx_pct))
351
352         results[ResultsConstants.MIN_LATENCY_NS] = (
353             '{:.3f}'.format(total_min_latency_ns))
354
355         results[ResultsConstants.MAX_LATENCY_NS] = (
356             '{:.3f}'.format(total_max_latency_ns))
357
358         results[ResultsConstants.AVG_LATENCY_NS] = (
359             '{:.3f}'.format(total_avg_latency_ns))
360
361         return results
362
363     def start_cont_traffic(self, traffic=None, duration=20):
364         """ Non-blocking version of 'send_cont_traffic'.
365
366         Start transmission and immediately return. Do not wait for
367         results.
368         :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
369         :param duration: Time to wait to receive packets (secs)
370         """
371         self._logger.info("In Moongen start_cont_traffic method")
372         return NotImplementedError('moongen continuous traffic not implemented')
373
374     def stop_cont_traffic(self):
375         # Stop continuous transmission and return results.
376         self._logger.info("In Moongen stop_cont_traffic method")
377
378     def run_moongen_and_collect_results(self, test_run=1):
379         """Execute Moongen and transform results into VSPERF format
380         :param test_run: The number of tests to run
381         """
382         # Start Moongen and create logfile of the run
383         connect_moongen = "ssh " + self._moongen_user + "@" + \
384             self._moongen_host_ip_addr
385
386         cmd_moongen = " 'cd " + self._moongen_base_dir + \
387             "; ./build/MoonGen examples/opnfv-vsperf.lua | tee moongen_log.txt'"
388
389         cmd_start_moongen = connect_moongen + cmd_moongen
390
391         start_moongen = subprocess.Popen(cmd_start_moongen,
392                                          shell=True, stderr=subprocess.PIPE)
393
394         output, error = start_moongen.communicate()
395
396         if start_moongen.returncode:
397             logging.debug(error)
398             logging.debug(output)
399             raise RuntimeError(
400                 'MOONGEN: Error starting Moongen program at %s within %s' \
401                 % (self._moongen_host_ip_addr, self._moongen_base_dir))
402
403         cmd_moongen = "mkdir -p /tmp/moongen/" + str(test_run)
404
405         moongen_create_log_dir = subprocess.Popen(cmd_moongen,
406                                                   shell=True,
407                                                   stderr=subprocess.PIPE)
408
409         output, error = moongen_create_log_dir.communicate()
410
411         if moongen_create_log_dir.returncode:
412             logging.debug(error)
413             logging.debug(output)
414             raise RuntimeError(
415                 'MOONGEN: Error obtaining Moongen log from %s within %s' \
416                 % (self._moongen_host_ip_addr, self._moongen_base_dir))
417
418         cmd_moongen = " scp " + self._moongen_user + "@" + \
419             self._moongen_host_ip_addr + ":" + \
420             self._moongen_base_dir + "/moongen_log.txt /tmp/moongen/" + \
421             str(test_run) + "/moongen-run.log"
422
423         copy_moongen_log = subprocess.Popen(cmd_moongen,
424                                             shell=True,
425                                             stderr=subprocess.PIPE)
426
427         output, error = copy_moongen_log.communicate()
428
429         if copy_moongen_log.returncode:
430             logging.debug(error)
431             logging.debug(output)
432             raise RuntimeError(
433                 'MOONGEN: Error obtaining Moongen log from %s within %s' \
434                 % (self._moongen_host_ip_addr, self._moongen_base_dir))
435
436         log_file = "/tmp/moongen/" + str(test_run) + "/moongen-run.log"
437
438         with open(log_file, 'r') as logfile_handle:
439             mytext = logfile_handle.read()
440
441              # REPORT results line
442              # match.group(1) = Tx frames
443              # match.group(2) = Rx frames
444              # match.group(3) = Frame loss (count)
445              # match.group(4) = Frame loss (percentage)
446              # match.group(5) = Tx Mpps
447              # match.group(6) = Rx Mpps
448             search_pattern = re.compile(
449                 r'\[REPORT\]\s+total\:\s+'
450                 r'Tx\s+frames\:\s+(\d+)\s+'
451                 r'Rx\s+Frames\:\s+(\d+)\s+'
452                 r'frame\s+loss\:\s+(\d+)\,'
453                 r'\s+(\d+\.\d+|\d+)%\s+'
454                 r'Tx\s+Mpps\:\s+(\d+.\d+|\d+)\s+'
455                 r'Rx\s+Mpps\:\s+(\d+\.\d+|\d+)',
456                 re.IGNORECASE)
457
458             results_match = search_pattern.search(mytext)
459
460             if not results_match:
461                 logging.error('There was a problem parsing ' +\
462                     'Moongen REPORT section of Moongen log file')
463
464             moongen_results = OrderedDict()
465             moongen_results[ResultsConstants.THROUGHPUT_RX_FPS] = 0
466             moongen_results[ResultsConstants.THROUGHPUT_RX_MBPS] = 0
467             moongen_results[ResultsConstants.THROUGHPUT_RX_PERCENT] = 0
468             moongen_results[ResultsConstants.TX_RATE_FPS] = 0
469             moongen_results[ResultsConstants.TX_RATE_MBPS] = 0
470             moongen_results[ResultsConstants.TX_RATE_PERCENT] = 0
471             moongen_results[ResultsConstants.B2B_TX_COUNT] = 0
472             moongen_results[ResultsConstants.B2B_FRAMES] = 0
473             moongen_results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = 0
474             moongen_results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = 0
475
476             # find PARAMETERS line
477             # parameters_match.group(1) = Frame size
478
479             search_pattern = re.compile(
480                 r'\[PARAMETERS\]\s+.*frameSize\:\s+(\d+)',
481                 flags=re.IGNORECASE)
482             parameters_match = search_pattern.search(mytext)
483
484             if parameters_match:
485                 frame_size = int(parameters_match.group(1))
486             else:
487                 logging.error('There was a problem parsing Moongen ' +\
488                     'PARAMETERS section of Moongen log file')
489                 frame_size = 0
490
491             # Each packet stream in the MoonGen report is prefaced with the
492             # words '[REPORT]Device'.  Count the instances of this string to
493             # get the total aggregrate throughput.  For example:
494             #
495             # - If num_traffic_streams = 1, there is a single
496             #                               unidirectional stream
497             #
498             # - If num_traffic_streams = 2, there is a bidirectional
499             #                               traffic stream
500             num_traffic_streams = mytext.count('[REPORT]Device')
501
502         if results_match and parameters_match and num_traffic_streams:
503             # Assume for now 10G link speed
504             max_theoretical_fps = (
505                 num_traffic_streams * (self._moongen_line_speed / 8) / (frame_size + 20))
506
507             moongen_results[ResultsConstants.THROUGHPUT_RX_FPS] = (
508                 float(results_match.group(6)) * 1000000)
509
510             moongen_results[ResultsConstants.THROUGHPUT_RX_MBPS] = (
511                 (float(results_match.group(6)) * frame_size + 20) * 8)
512
513             moongen_results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
514                 (100 * float(results_match.group(6)) * 1000000) / max_theoretical_fps)
515
516             moongen_results[ResultsConstants.TX_RATE_FPS] = (
517                 float(results_match.group(5)) * 1000000)
518
519             moongen_results[ResultsConstants.TX_RATE_MBPS] = (
520                 float(results_match.group(5)) * (frame_size + 20) * 8)
521
522             moongen_results[ResultsConstants.TX_RATE_PERCENT] = (
523                 (100 * float(results_match.group(5)) * 1000000) / max_theoretical_fps)
524
525             moongen_results[ResultsConstants.B2B_TX_COUNT] = (
526                 float(results_match.group(1)))
527
528             moongen_results[ResultsConstants.B2B_FRAMES] = (
529                 float(results_match.group(2)))
530
531             moongen_results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = (
532                 float(results_match.group(3)))
533
534             moongen_results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = (
535                 float(results_match.group(4)))
536
537         return moongen_results
538
539     def send_rfc2544_throughput(self, traffic=None, duration=20,
540                                 lossrate=0.0, tests=1):
541         #
542         # Send traffic per RFC2544 throughput test specifications.
543         #
544         # Send packets at a variable rate, using ``traffic``
545         # configuration, until minimum rate at which no packet loss is
546         # detected is found.
547         #
548         # :param traffic: Detailed "traffic" spec, see design docs for details
549         # :param tests: Number of tests to execute
550         # :param duration: Per iteration duration
551         # :param lossrate: Acceptable lossrate percentage
552         # :returns: dictionary of strings with following data:
553         #     - Tx Throughput (fps),
554         #     - Rx Throughput (fps),
555         #     - Tx Throughput (mbps),
556         #     - Rx Throughput (mbps),
557         #     - Tx Throughput (% linerate),
558         #     - Rx Throughput (% linerate),
559         #     - Min Latency (ns),
560         #     - Max Latency (ns),
561         #     - Avg Latency (ns)
562         #
563         self._logger.info("In moongen send_rfc2544_throughput method")
564         self._params.clear()
565         self._params['traffic'] = self.traffic_defaults.copy()
566
567         if traffic:
568             self._params['traffic'] = merge_spec(self._params['traffic'],
569                                                  traffic)
570         Moongen.create_moongen_cfg_file(self,
571                                         traffic,
572                                         duration=duration,
573                                         acceptable_loss_pct=lossrate)
574
575         total_throughput_rx_fps = 0
576         total_throughput_rx_mbps = 0
577         total_throughput_rx_pct = 0
578         total_throughput_tx_fps = 0
579         total_throughput_tx_mbps = 0
580         total_throughput_tx_pct = 0
581         total_min_latency_ns = 0
582         total_max_latency_ns = 0
583         total_avg_latency_ns = 0
584
585         for test_run in range(1, tests+1):
586             collected_results = (
587                 Moongen.run_moongen_and_collect_results(self, test_run=test_run))
588
589             total_throughput_rx_fps += (
590                 float(collected_results[ResultsConstants.THROUGHPUT_RX_FPS]))
591
592             total_throughput_rx_mbps += (
593                 float(collected_results[ResultsConstants.THROUGHPUT_RX_MBPS]))
594
595             total_throughput_rx_pct += (
596                 float(collected_results[ResultsConstants.THROUGHPUT_RX_PERCENT]))
597
598             total_throughput_tx_fps += (
599                 float(collected_results[ResultsConstants.TX_RATE_FPS]))
600
601             total_throughput_tx_mbps += (
602                 float(collected_results[ResultsConstants.TX_RATE_MBPS]))
603
604             total_throughput_tx_pct += (
605                 float(collected_results[ResultsConstants.TX_RATE_PERCENT]))
606
607             # Latency not supported now, leaving as placeholder
608             total_min_latency_ns = 0
609             total_max_latency_ns = 0
610             total_avg_latency_ns = 0
611
612         results = OrderedDict()
613         results[ResultsConstants.THROUGHPUT_RX_FPS] = (
614             '{:.6f}'.format(total_throughput_rx_fps / tests))
615
616         results[ResultsConstants.THROUGHPUT_RX_MBPS] = (
617             '{:.3f}'.format(total_throughput_rx_mbps / tests))
618
619         results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
620             '{:.3f}'.format(total_throughput_rx_pct / tests))
621
622         results[ResultsConstants.TX_RATE_FPS] = (
623             '{:.6f}'.format(total_throughput_tx_fps / tests))
624
625         results[ResultsConstants.TX_RATE_MBPS] = (
626             '{:.3f}'.format(total_throughput_tx_mbps / tests))
627
628         results[ResultsConstants.TX_RATE_PERCENT] = (
629             '{:.3f}'.format(total_throughput_tx_pct / tests))
630
631         results[ResultsConstants.MIN_LATENCY_NS] = (
632             '{:.3f}'.format(total_min_latency_ns / tests))
633
634         results[ResultsConstants.MAX_LATENCY_NS] = (
635             '{:.3f}'.format(total_max_latency_ns / tests))
636
637         results[ResultsConstants.AVG_LATENCY_NS] = (
638             '{:.3f}'.format(total_avg_latency_ns / tests))
639
640         return results
641
642     def start_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
643                                  lossrate=0.0):
644         """Non-blocking version of 'send_rfc2544_throughput'.
645
646         Start transmission and immediately return. Do not wait for
647         results.
648         """
649         self._logger.info(
650             "MOONGEN: In moongen start_rfc2544_throughput method")
651
652     def wait_rfc2544_throughput(self):
653         """Wait for and return results of RFC2544 test.
654         """
655         self._logger.info('In moongen wait_rfc2544_throughput')
656
657     def send_rfc2544_back2back(self, traffic=None, duration=60,
658                                lossrate=0.0, tests=1):
659         """Send traffic per RFC2544 back2back test specifications.
660
661         Send packets at a fixed rate, using ``traffic``
662         configuration, for duration seconds.
663
664         :param traffic: Detailed "traffic" spec, see design docs for details
665         :param tests: Number of tests to execute
666         :param duration: Per iteration duration
667         :param lossrate: Acceptable loss percentage
668
669         :returns: Named tuple of Rx Throughput (fps), Rx Throughput (mbps),
670             Tx Rate (% linerate), Rx Rate (% linerate), Tx Count (frames),
671             Back to Back Count (frames), Frame Loss (frames), Frame Loss (%)
672         :rtype: :class:`Back2BackResult`
673         """
674         self._params.clear()
675         self._params['traffic'] = self.traffic_defaults.copy()
676
677         if traffic:
678             self._params['traffic'] = merge_spec(self._params['traffic'],
679                                                  traffic)
680
681         Moongen.create_moongen_cfg_file(self,
682                                         traffic,
683                                         duration=duration,
684                                         acceptable_loss_pct=lossrate)
685
686         results = OrderedDict()
687         results[ResultsConstants.B2B_RX_FPS] = 0
688         results[ResultsConstants.B2B_TX_FPS] = 0
689         results[ResultsConstants.B2B_RX_PERCENT] = 0
690         results[ResultsConstants.B2B_TX_PERCENT] = 0
691         results[ResultsConstants.B2B_TX_COUNT] = 0
692         results[ResultsConstants.B2B_FRAMES] = 0
693         results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = 0
694         results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = 0
695         results[ResultsConstants.SCAL_STREAM_COUNT] = 0
696         results[ResultsConstants.SCAL_STREAM_TYPE] = 0
697         results[ResultsConstants.SCAL_PRE_INSTALLED_FLOWS] = 0
698
699         for test_run in range(1, tests+1):
700             collected_results = (
701                 Moongen.run_moongen_and_collect_results(self, test_run=test_run))
702
703             results[ResultsConstants.B2B_RX_FPS] += (
704                 float(collected_results[ResultsConstants.THROUGHPUT_RX_FPS]))
705
706             results[ResultsConstants.B2B_RX_PERCENT] += (
707                 float(collected_results[ResultsConstants.THROUGHPUT_RX_PERCENT]))
708
709             results[ResultsConstants.B2B_TX_FPS] += (
710                 float(collected_results[ResultsConstants.TX_RATE_FPS]))
711
712             results[ResultsConstants.B2B_TX_PERCENT] += (
713                 float(collected_results[ResultsConstants.TX_RATE_PERCENT]))
714
715             results[ResultsConstants.B2B_TX_COUNT] += (
716                 int(collected_results[ResultsConstants.B2B_TX_COUNT]))
717
718             results[ResultsConstants.B2B_FRAMES] += (
719                 int(collected_results[ResultsConstants.B2B_FRAMES]))
720
721             results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] += (
722                 int(collected_results[ResultsConstants.B2B_FRAME_LOSS_FRAMES]))
723
724             results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] += (
725                 int(collected_results[ResultsConstants.B2B_FRAME_LOSS_PERCENT]))
726
727         # Calculate average results
728         results[ResultsConstants.B2B_RX_FPS] = (
729             results[ResultsConstants.B2B_RX_FPS] / tests)
730
731         results[ResultsConstants.B2B_RX_PERCENT] = (
732             results[ResultsConstants.B2B_RX_PERCENT] / tests)
733
734         results[ResultsConstants.B2B_TX_FPS] = (
735             results[ResultsConstants.B2B_TX_FPS] / tests)
736
737         results[ResultsConstants.B2B_TX_PERCENT] = (
738             results[ResultsConstants.B2B_TX_PERCENT] / tests)
739
740         results[ResultsConstants.B2B_TX_COUNT] = (
741             results[ResultsConstants.B2B_TX_COUNT] / tests)
742
743         results[ResultsConstants.B2B_FRAMES] = (
744             results[ResultsConstants.B2B_FRAMES] / tests)
745
746         results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = (
747             results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] / tests)
748
749         results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = (
750             results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] / tests)
751
752         results[ResultsConstants.SCAL_STREAM_COUNT] = 0
753         results[ResultsConstants.SCAL_STREAM_TYPE] = 0
754         results[ResultsConstants.SCAL_PRE_INSTALLED_FLOWS] = 0
755
756         return results
757
758     def start_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
759                                 lossrate=0.0):
760         #
761         # Non-blocking version of 'send_rfc2544_back2back'.
762         #
763         # Start transmission and immediately return. Do not wait for results.
764         #
765         self._logger.info("In Moongen start_rfc2544_back2back method")
766         return NotImplementedError(
767             'Moongen start back2back traffic not implemented')
768
769     def wait_rfc2544_back2back(self):
770         self._logger.info("In moongen wait_rfc2544_back2back method")
771         #
772         # Wait and set results of RFC2544 test.
773         #
774         return NotImplementedError(
775             'Moongen wait back2back traffic not implemented')
776
777 if __name__ == "__main__":
778     pass