pkt_gen: MoonGen start rates not correct
[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         if results_match and parameters_match:
492             # Assume for now 10G link speed
493             max_theoretical_mfps = (
494                 (self._moongen_line_speed / 8) / (frame_size + 20))
495
496             moongen_results[ResultsConstants.THROUGHPUT_RX_FPS] = (
497                 float(results_match.group(6)) * 1000000)
498
499             moongen_results[ResultsConstants.THROUGHPUT_RX_MBPS] = (
500                 (float(results_match.group(6)) * frame_size + 20) * 8)
501
502             moongen_results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
503                 float(results_match.group(6)) * \
504                       1000000 / max_theoretical_mfps * 100)
505
506             moongen_results[ResultsConstants.TX_RATE_FPS] = (
507                 float(results_match.group(5)) * 1000000)
508
509             moongen_results[ResultsConstants.TX_RATE_MBPS] = (
510                 float(results_match.group(5)) * (frame_size + 20) * 8)
511
512             moongen_results[ResultsConstants.TX_RATE_PERCENT] = (
513                 float(results_match.group(5)) *
514                 1000000 / max_theoretical_mfps * 100)
515
516             moongen_results[ResultsConstants.B2B_TX_COUNT] = (
517                 float(results_match.group(1)))
518
519             moongen_results[ResultsConstants.B2B_FRAMES] = (
520                 float(results_match.group(2)))
521
522             moongen_results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = (
523                 float(results_match.group(3)))
524
525             moongen_results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = (
526                 float(results_match.group(4)))
527
528         return moongen_results
529
530     def send_rfc2544_throughput(self, traffic=None, duration=20,
531                                 lossrate=0.0, trials=1):
532         #
533         # Send traffic per RFC2544 throughput test specifications.
534         #
535         # Send packets at a variable rate, using ``traffic``
536         # configuration, until minimum rate at which no packet loss is
537         # detected is found.
538         #
539         # :param traffic: Detailed "traffic" spec, see design docs for details
540         # :param trials: Number of trials to execute
541         # :param duration: Per iteration duration
542         # :param lossrate: Acceptable lossrate percentage
543         # :returns: dictionary of strings with following data:
544         #     - Tx Throughput (fps),
545         #     - Rx Throughput (fps),
546         #     - Tx Throughput (mbps),
547         #     - Rx Throughput (mbps),
548         #     - Tx Throughput (% linerate),
549         #     - Rx Throughput (% linerate),
550         #     - Min Latency (ns),
551         #     - Max Latency (ns),
552         #     - Avg Latency (ns)
553         #
554         self._logger.info("In moongen send_rfc2544_throughput method")
555         self._params.clear()
556         self._params['traffic'] = self.traffic_defaults.copy()
557
558         if traffic:
559             self._params['traffic'] = merge_spec(self._params['traffic'],
560                                                  traffic)
561         Moongen.create_moongen_cfg_file(self,
562                                         traffic,
563                                         duration=duration,
564                                         acceptable_loss_pct=lossrate)
565
566         total_throughput_rx_fps = 0
567         total_throughput_rx_mbps = 0
568         total_throughput_rx_pct = 0
569         total_throughput_tx_fps = 0
570         total_throughput_tx_mbps = 0
571         total_throughput_tx_pct = 0
572         total_min_latency_ns = 0
573         total_max_latency_ns = 0
574         total_avg_latency_ns = 0
575
576         for test_run in range(1, trials+1):
577             collected_results = (
578                 Moongen.run_moongen_and_collect_results(self, test_run=test_run))
579
580             total_throughput_rx_fps += (
581                 float(collected_results[ResultsConstants.THROUGHPUT_RX_FPS]))
582
583             total_throughput_rx_mbps += (
584                 float(collected_results[ResultsConstants.THROUGHPUT_RX_MBPS]))
585
586             total_throughput_rx_pct += (
587                 float(collected_results[ResultsConstants.THROUGHPUT_RX_PERCENT]))
588
589             total_throughput_tx_fps += (
590                 float(collected_results[ResultsConstants.TX_RATE_FPS]))
591
592             total_throughput_tx_mbps += (
593                 float(collected_results[ResultsConstants.TX_RATE_MBPS]))
594
595             total_throughput_tx_pct += (
596                 float(collected_results[ResultsConstants.TX_RATE_PERCENT]))
597
598             # Latency not supported now, leaving as placeholder
599             total_min_latency_ns = 0
600             total_max_latency_ns = 0
601             total_avg_latency_ns = 0
602
603         results = OrderedDict()
604         results[ResultsConstants.THROUGHPUT_RX_FPS] = (
605             '{:.6f}'.format(total_throughput_rx_fps / trials))
606
607         results[ResultsConstants.THROUGHPUT_RX_MBPS] = (
608             '{:.3f}'.format(total_throughput_rx_mbps / trials))
609
610         results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
611             '{:.3f}'.format(total_throughput_rx_pct / trials))
612
613         results[ResultsConstants.TX_RATE_FPS] = (
614             '{:.6f}'.format(total_throughput_tx_fps / trials))
615
616         results[ResultsConstants.TX_RATE_MBPS] = (
617             '{:.3f}'.format(total_throughput_tx_mbps / trials))
618
619         results[ResultsConstants.TX_RATE_PERCENT] = (
620             '{:.3f}'.format(total_throughput_tx_pct / trials))
621
622         results[ResultsConstants.MIN_LATENCY_NS] = (
623             '{:.3f}'.format(total_min_latency_ns / trials))
624
625         results[ResultsConstants.MAX_LATENCY_NS] = (
626             '{:.3f}'.format(total_max_latency_ns / trials))
627
628         results[ResultsConstants.AVG_LATENCY_NS] = (
629             '{:.3f}'.format(total_avg_latency_ns / trials))
630
631         return results
632
633     def start_rfc2544_throughput(self, traffic=None, trials=3, duration=20,
634                                  lossrate=0.0):
635         """Non-blocking version of 'send_rfc2544_throughput'.
636
637         Start transmission and immediately return. Do not wait for
638         results.
639         """
640         self._logger.info(
641             "MOONGEN: In moongen start_rfc2544_throughput method")
642
643     def wait_rfc2544_throughput(self):
644         """Wait for and return results of RFC2544 test.
645         """
646         self._logger.info('In moongen wait_rfc2544_throughput')
647
648     def send_rfc2544_back2back(self, traffic=None, duration=60,
649                                lossrate=0.0, trials=1):
650         """Send traffic per RFC2544 back2back test specifications.
651
652         Send packets at a fixed rate, using ``traffic``
653         configuration, for duration seconds.
654
655         :param traffic: Detailed "traffic" spec, see design docs for details
656         :param trials: Number of trials to execute
657         :param duration: Per iteration duration
658         :param lossrate: Acceptable loss percentage
659
660         :returns: Named tuple of Rx Throughput (fps), Rx Throughput (mbps),
661             Tx Rate (% linerate), Rx Rate (% linerate), Tx Count (frames),
662             Back to Back Count (frames), Frame Loss (frames), Frame Loss (%)
663         :rtype: :class:`Back2BackResult`
664         """
665         self._params.clear()
666         self._params['traffic'] = self.traffic_defaults.copy()
667
668         if traffic:
669             self._params['traffic'] = merge_spec(self._params['traffic'],
670                                                  traffic)
671
672         Moongen.create_moongen_cfg_file(self,
673                                         traffic,
674                                         duration=duration,
675                                         acceptable_loss_pct=lossrate)
676
677         results = OrderedDict()
678         results[ResultsConstants.B2B_RX_FPS] = 0
679         results[ResultsConstants.B2B_TX_FPS] = 0
680         results[ResultsConstants.B2B_RX_PERCENT] = 0
681         results[ResultsConstants.B2B_TX_PERCENT] = 0
682         results[ResultsConstants.B2B_TX_COUNT] = 0
683         results[ResultsConstants.B2B_FRAMES] = 0
684         results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = 0
685         results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = 0
686         results[ResultsConstants.SCAL_STREAM_COUNT] = 0
687         results[ResultsConstants.SCAL_STREAM_TYPE] = 0
688         results[ResultsConstants.SCAL_PRE_INSTALLED_FLOWS] = 0
689
690         for test_run in range(1, trials+1):
691             collected_results = (
692                 Moongen.run_moongen_and_collect_results(self, test_run=test_run))
693
694             results[ResultsConstants.B2B_RX_FPS] += (
695                 float(collected_results[ResultsConstants.THROUGHPUT_RX_FPS]))
696
697             results[ResultsConstants.B2B_RX_PERCENT] += (
698                 float(collected_results[ResultsConstants.THROUGHPUT_RX_PERCENT]))
699
700             results[ResultsConstants.B2B_TX_FPS] += (
701                 float(collected_results[ResultsConstants.TX_RATE_FPS]))
702
703             results[ResultsConstants.B2B_TX_PERCENT] += (
704                 float(collected_results[ResultsConstants.TX_RATE_PERCENT]))
705
706             results[ResultsConstants.B2B_TX_COUNT] += (
707                 int(collected_results[ResultsConstants.B2B_TX_COUNT]))
708
709             results[ResultsConstants.B2B_FRAMES] += (
710                 int(collected_results[ResultsConstants.B2B_FRAMES]))
711
712             results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] += (
713                 int(collected_results[ResultsConstants.B2B_FRAME_LOSS_FRAMES]))
714
715             results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] += (
716                 int(collected_results[ResultsConstants.B2B_FRAME_LOSS_PERCENT]))
717
718         # Calculate average results
719         results[ResultsConstants.B2B_RX_FPS] = (
720             results[ResultsConstants.B2B_RX_FPS] / trials)
721
722         results[ResultsConstants.B2B_RX_PERCENT] = (
723             results[ResultsConstants.B2B_RX_PERCENT] / trials)
724
725         results[ResultsConstants.B2B_TX_FPS] = (
726             results[ResultsConstants.B2B_TX_FPS] / trials)
727
728         results[ResultsConstants.B2B_TX_PERCENT] = (
729             results[ResultsConstants.B2B_TX_PERCENT] / trials)
730
731         results[ResultsConstants.B2B_TX_COUNT] = (
732             results[ResultsConstants.B2B_TX_COUNT] / trials)
733
734         results[ResultsConstants.B2B_FRAMES] = (
735             results[ResultsConstants.B2B_FRAMES] / trials)
736
737         results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = (
738             results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] / trials)
739
740         results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = (
741             results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] / trials)
742
743         results[ResultsConstants.SCAL_STREAM_COUNT] = 0
744         results[ResultsConstants.SCAL_STREAM_TYPE] = 0
745         results[ResultsConstants.SCAL_PRE_INSTALLED_FLOWS] = 0
746
747         return results
748
749     def start_rfc2544_back2back(self, traffic=None, trials=1, duration=20,
750                                 lossrate=0.0):
751         #
752         # Non-blocking version of 'send_rfc2544_back2back'.
753         #
754         # Start transmission and immediately return. Do not wait for results.
755         #
756         self._logger.info("In Moongen start_rfc2544_back2back method")
757         return NotImplementedError(
758             'Moongen start back2back traffic not implemented')
759
760     def wait_rfc2544_back2back(self):
761         self._logger.info("In moongen wait_rfc2544_back2back method")
762         #
763         # Wait and set results of RFC2544 test.
764         #
765         return NotImplementedError(
766             'Moongen wait back2back traffic not implemented')
767
768 if __name__ == "__main__":
769     pass