# Copyright 2016 Red Hat Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Contributors:
#   Bill Michalowski, Red Hat Inc.
#   Andrew Theurer, Red Hat Inc.
"""
Moongen Traffic Generator Model
"""

# python imports
from collections import OrderedDict
import logging
import math
import re
import subprocess

# VSPerf imports
from conf import settings
from conf import merge_spec
from core.results.results_constants import ResultsConstants
from tools.pkt_gen.trafficgen.trafficgen import ITrafficGenerator

class Moongen(ITrafficGenerator):
    """Moongen Traffic generator wrapper."""
    _logger = logging.getLogger(__name__)

    def __init__(self):
        """Moongen class constructor."""
        super().__init__()
        self._logger.info("In moongen __init__ method")
        self._params = {}
        self._moongen_host_ip_addr = (
            settings.getValue('TRAFFICGEN_MOONGEN_HOST_IP_ADDR'))
        self._moongen_base_dir = (
            settings.getValue('TRAFFICGEN_MOONGEN_BASE_DIR'))
        self._moongen_user = settings.getValue('TRAFFICGEN_MOONGEN_USER')
        self._moongen_ports = settings.getValue('TRAFFICGEN_MOONGEN_PORTS')

        if settings.getValue('TRAFFICGEN_MOONGEN_LINE_SPEED_GBPS') == '10':
            self._moongen_line_speed = math.pow(10, 10)
        else:
            raise RuntimeError(
                'MOONGEN: Invalid line speed in configuration ' + \
                'file (today 10Gbps supported)')

    def create_moongen_cfg_file(self, traffic, duration=60,
                                acceptable_loss_pct=1, one_shot=0):
        """Create the Moongen configuration file from VSPERF's traffic profile
        :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
        :param duration: The length of time to generate packet throughput
        :param acceptable_loss: Maximum packet loss acceptable
        :param one_shot: No RFC 2544 binary search,
                        just packet flow at traffic specifics
        """
        logging.debug("traffic['frame_rate'] = %s", \
            str(traffic['frame_rate']))

        logging.debug("traffic['multistream'] = %s", \
            str(traffic['multistream']))

        logging.debug("traffic['stream_type'] = %s", \
            str(traffic['stream_type']))

        logging.debug("traffic['l2']['srcmac'] = %s", \
            str(traffic['l2']['srcmac']))

        logging.debug("traffic['l2']['dstmac'] = %s", \
            str(traffic['l2']['dstmac']))

        logging.debug("traffic['l3']['proto'] = %s", \
            str(traffic['l3']['proto']))

        logging.debug("traffic['l3']['srcip'] = %s", \
            str(traffic['l3']['srcip']))

        logging.debug("traffic['l3']['dstip'] = %s", \
            str(traffic['l3']['dstip']))

        logging.debug("traffic['l4']['srcport'] = %s", \
            str(traffic['l4']['srcport']))

        logging.debug("traffic['l4']['dstport'] = %s", \
            str(traffic['l4']['dstport']))

        logging.debug("traffic['vlan']['enabled'] = %s", \
            str(traffic['vlan']['enabled']))

        logging.debug("traffic['vlan']['id'] = %s", \
            str(traffic['vlan']['id']))

        logging.debug("traffic['vlan']['priority'] = %s", \
            str(traffic['vlan']['priority']))

        logging.debug("traffic['vlan']['cfi'] = %s", \
            str(traffic['vlan']['cfi']))

        logging.debug(traffic['l2']['framesize'])

        out_file = open("opnfv-vsperf-cfg.lua", "wt")

        out_file.write("VSPERF {\n")

        out_file.write("testType = \"throughput\",\n")

        out_file.write("nrFlows = " + \
            str(traffic['multistream']) + ",\n")

        out_file.write("runBidirec = " + \
            traffic['bidir'].lower() + ",\n")

        out_file.write("frameSize = " + \
            str(traffic['l2']['framesize']) + ",\n")

        out_file.write("srcMac = \"" + \
            str(traffic['l2']['srcmac']) + "\",\n")

        out_file.write("dstMac = \"" + \
            str(traffic['l2']['dstmac']) + "\",\n")

        out_file.write("srcIp = \"" + \
            str(traffic['l3']['srcip']) + "\",\n")

        out_file.write("dstIp = \"" + \
            str(traffic['l3']['dstip']) + "\",\n")

        if traffic['vlan']['enabled']:
            out_file.write("vlanId = " + \
                str(traffic['vlan']['id']) + ",\n")

        out_file.write("searchRunTime = " + \
            str(duration) + ",\n")

        out_file.write("validationRunTime = " + \
            str(duration) + ",\n")

        out_file.write("acceptableLossPct = " + \
            str(acceptable_loss_pct) + ",\n")

        out_file.write("ports = " +\
            str(self._moongen_ports) +  ",\n")

        if one_shot:
            out_file.write("oneShot = true,\n")

        # Need to convert VSPERF frame_rate (percentage of line rate)
        # to Mpps for Moongen
        start_rate = str(
            (traffic['frame_rate'] / 100) * (self._moongen_line_speed / \
            (8 * (traffic['l2']['framesize'] + 20)) / math.pow(10, 6)))

        logging.debug("startRate = %s", start_rate)

        out_file.write("startRate = %s" % \
            start_rate + "\n")

        out_file.write("}" + "\n")
        out_file.close()

        copy_moongen_cfg = "scp opnfv-vsperf-cfg.lua " + \
                            self._moongen_user + "@" + \
                            self._moongen_host_ip_addr + ":" + \
                            self._moongen_base_dir + \
                            "/. && rm opnfv-vsperf-cfg.lua"

        find_moongen = subprocess.Popen(copy_moongen_cfg,
                                        shell=True,
                                        stderr=subprocess.PIPE)

        output, error = find_moongen.communicate()

        if error:
            logging.error(output)
            logging.error(error)
            raise RuntimeError('MOONGEN: Error copying configuration file')

    def connect(self):
        """Connect to Moongen traffic generator

        Verify that Moongen is on the system indicated by
        the configuration file
        """
        self._logger.info("MOONGEN:  In Moongen connect method...")

        if self._moongen_host_ip_addr:
            cmd_ping = "ping -c1 " + self._moongen_host_ip_addr
        else:
            raise RuntimeError('MOONGEN: Moongen host not defined')

        ping = subprocess.Popen(cmd_ping, shell=True, stderr=subprocess.PIPE)
        output, error = ping.communicate()

        if ping.returncode:
            self._logger.error(error)
            self._logger.error(output)
            raise RuntimeError('MOONGEN: Cannot ping Moongen host at ' + \
                               self._moongen_host_ip_addr)

        connect_moongen = "ssh " + self._moongen_user + \
                          "@" + self._moongen_host_ip_addr

        cmd_find_moongen = connect_moongen + " ls " + \
                           self._moongen_base_dir + "/trafficgen.lua"

        find_moongen = subprocess.Popen(cmd_find_moongen,
                                        shell=True,
                                        stderr=subprocess.PIPE)

        output, error = find_moongen.communicate()

        if find_moongen.returncode:
            self._logger.error(error)
            self._logger.error(output)
            raise RuntimeError(
                'MOONGEN: Cannot locate Moongen program at %s within %s' \
                % (self._moongen_host_ip_addr, self._moongen_base_dir))

        self._logger.info("MOONGEN: Moongen host successfully found...")

    def disconnect(self):
        """Disconnect from the traffic generator.

        As with :func:`connect`, this function is optional.

        Where implemented, this function should raise an exception on
        failure.

        :returns: None
        """
        self._logger.info("MOONGEN: In moongen disconnect method")

    def send_burst_traffic(self, traffic=None, duration=20):
        """Send a burst of traffic.

        Send a ``traffic['burst_traffic']`` packets of traffic, using ``traffic``
        configuration, with a timeout of ``time``.

        :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
        :param duration: Time to wait to receive packets

        :returns: dictionary of strings with following data:
            - List of Tx Frames,
            - List of Rx Frames,
            - List of Tx Bytes,
            - List of List of Rx Bytes,
            - Payload Errors and Sequence Errors.
        """
        self._logger.info("In Moongen send_burst_traffic method")
        return NotImplementedError('Moongen Burst traffic not implemented')

    def send_cont_traffic(self, traffic=None, duration=20):
        """Send a continuous flow of traffic

        Send packets at ``frame rate``, using ``traffic`` configuration,
        until timeout ``time`` occurs.

        :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
        :param duration: Time to wait to receive packets (secs)
        :returns: dictionary of strings with following data:
            - Tx Throughput (fps),
            - Rx Throughput (fps),
            - Tx Throughput (mbps),
            - Rx Throughput (mbps),
            - Tx Throughput (% linerate),
            - Rx Throughput (% linerate),
            - Min Latency (ns),
            - Max Latency (ns),
            - Avg Latency (ns)
        """
        self._logger.info("In Moongen send_cont_traffic method")

        self._params.clear()
        self._params['traffic'] = self.traffic_defaults.copy()

        if traffic:
            self._params['traffic'] = merge_spec(self._params['traffic'],
                                                 traffic)

        Moongen.create_moongen_cfg_file(self,
                                        traffic,
                                        duration=duration,
                                        acceptable_loss_pct=100.0,
                                        one_shot=1)

        collected_results = Moongen.run_moongen_and_collect_results(self,
                                                                    test_run=1)

        results = OrderedDict()

        results[ResultsConstants.THROUGHPUT_RX_FPS] = (
            '{:.6f}'.format(
                float(collected_results[ResultsConstants.THROUGHPUT_RX_FPS])))

        results[ResultsConstants.THROUGHPUT_RX_MBPS] = (
            '{:.3f}'.format(
                float(collected_results[ResultsConstants.THROUGHPUT_RX_MBPS])))

        results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
            '{:.3f}'.format(
                float(
                    collected_results[ResultsConstants.THROUGHPUT_RX_PERCENT])))

        results[ResultsConstants.TX_RATE_FPS] = (
            '{:.3f}'.format(
                float(collected_results[ResultsConstants.TX_RATE_FPS])))

        results[ResultsConstants.TX_RATE_MBPS] = (
            '{:.3f}'.format(
                float(collected_results[ResultsConstants.TX_RATE_MBPS])))

        results[ResultsConstants.TX_RATE_PERCENT] = (
            '{:.3f}'.format(
                float(collected_results[ResultsConstants.TX_RATE_PERCENT])))

        results[ResultsConstants.MIN_LATENCY_NS] = 0

        results[ResultsConstants.MAX_LATENCY_NS] = 0

        results[ResultsConstants.AVG_LATENCY_NS] = 0

        return results

    def start_cont_traffic(self, traffic=None, duration=20):
        """ Non-blocking version of 'send_cont_traffic'.

        Start transmission and immediately return. Do not wait for
        results.
        :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
        :param duration: Time to wait to receive packets (secs)
        """
        self._logger.info("In Moongen start_cont_traffic method")
        return NotImplementedError('moongen continuous traffic not implemented')

    def stop_cont_traffic(self):
        # Stop continuous transmission and return results.
        self._logger.info("In Moongen stop_cont_traffic method")

    def run_moongen_and_collect_results(self, test_run=1):
        """Execute Moongen and transform results into VSPERF format
        :param test_run: The number of tests to run
        """
        # Start Moongen and create logfile of the run
        connect_moongen = "ssh " + self._moongen_user + "@" + \
            self._moongen_host_ip_addr

        cmd_moongen = " 'cd " + self._moongen_base_dir + \
            "; ./MoonGen/build/MoonGen trafficgen.lua | tee moongen_log.txt'"

        cmd_start_moongen = connect_moongen + cmd_moongen

        start_moongen = subprocess.Popen(cmd_start_moongen,
                                         shell=True, stderr=subprocess.PIPE)

        output, error = start_moongen.communicate()

        if start_moongen.returncode:
            logging.debug(error)
            logging.debug(output)
            raise RuntimeError(
                'MOONGEN: Error starting Moongen program at %s within %s' \
                % (self._moongen_host_ip_addr, self._moongen_base_dir))

        cmd_moongen = "mkdir -p /tmp/moongen/" + str(test_run)

        moongen_create_log_dir = subprocess.Popen(cmd_moongen,
                                                  shell=True,
                                                  stderr=subprocess.PIPE)

        output, error = moongen_create_log_dir.communicate()

        if moongen_create_log_dir.returncode:
            logging.debug(error)
            logging.debug(output)
            raise RuntimeError(
                'MOONGEN: Error obtaining Moongen log from %s within %s' \
                % (self._moongen_host_ip_addr, self._moongen_base_dir))

        cmd_moongen = " scp " + self._moongen_user + "@" + \
            self._moongen_host_ip_addr + ":" + \
            self._moongen_base_dir + "/moongen_log.txt /tmp/moongen/" + \
            str(test_run) + "/moongen-run.log"

        copy_moongen_log = subprocess.Popen(cmd_moongen,
                                            shell=True,
                                            stderr=subprocess.PIPE)

        output, error = copy_moongen_log.communicate()

        if copy_moongen_log.returncode:
            logging.debug(error)
            logging.debug(output)
            raise RuntimeError(
                'MOONGEN: Error obtaining Moongen log from %s within %s' \
                % (self._moongen_host_ip_addr, self._moongen_base_dir))

        log_file = "/tmp/moongen/" + str(test_run) + "/moongen-run.log"

        with open(log_file, 'r') as logfile_handle:
            mytext = logfile_handle.read()

             # REPORT results line
             # match.group(1) = Tx frames
             # match.group(2) = Rx frames
             # match.group(3) = Frame loss (count)
             # match.group(4) = Frame loss (percentage)
             # match.group(5) = Tx Mpps
             # match.group(6) = Rx Mpps
            search_pattern = re.compile(
                r'\[REPORT\]\s+total\:\s+'
                r'Tx\s+frames\:\s+(\d+)\s+'
                r'Rx\s+Frames\:\s+(\d+)\s+'
                r'frame\s+loss\:\s+(\d+)\,'
                r'\s+(\d+\.\d+|\d+)%\s+'
                r'Tx\s+Mpps\:\s+(\d+.\d+|\d+)\s+'
                r'Rx\s+Mpps\:\s+(\d+\.\d+|\d+)',
                re.IGNORECASE)

            results_match = search_pattern.search(mytext)

            if not results_match:
                logging.error('There was a problem parsing ' +\
                    'Moongen REPORT section of Moongen log file')

            moongen_results = OrderedDict()
            moongen_results[ResultsConstants.THROUGHPUT_RX_FPS] = 0
            moongen_results[ResultsConstants.THROUGHPUT_RX_MBPS] = 0
            moongen_results[ResultsConstants.THROUGHPUT_RX_PERCENT] = 0
            moongen_results[ResultsConstants.TX_RATE_FPS] = 0
            moongen_results[ResultsConstants.TX_RATE_MBPS] = 0
            moongen_results[ResultsConstants.TX_RATE_PERCENT] = 0
            moongen_results[ResultsConstants.B2B_TX_COUNT] = 0
            moongen_results[ResultsConstants.B2B_FRAMES] = 0
            moongen_results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = 0
            moongen_results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = 0

            # find PARAMETERS line
            # parameters_match.group(1) = Frame size

            search_pattern = re.compile(
                r'\[PARAMETERS\]\s+.*frameSize\:\s+(\d+)',
                flags=re.IGNORECASE)
            parameters_match = search_pattern.search(mytext)

            if parameters_match:
                frame_size = int(parameters_match.group(1))
            else:
                logging.error('There was a problem parsing Moongen ' +\
                    'PARAMETERS section of Moongen log file')
                frame_size = 0

            # Each packet stream in the MoonGen report is prefaced with the
            # words '[REPORT]Device'.  Count the instances of this string to
            # get the total aggregrate throughput.  For example:
            #
            # - If num_traffic_streams = 1, there is a single
            #                               unidirectional stream
            #
            # - If num_traffic_streams = 2, there is a bidirectional
            #                               traffic stream
            num_traffic_streams = mytext.count('[REPORT]Device')

        if results_match and parameters_match and num_traffic_streams:
            # Assume for now 10G link speed
            max_theoretical_fps = (
                num_traffic_streams * (self._moongen_line_speed / 8) / (frame_size + 20))

            moongen_results[ResultsConstants.THROUGHPUT_RX_FPS] = (
                float(results_match.group(6)) * 1000000)

            moongen_results[ResultsConstants.THROUGHPUT_RX_MBPS] = (
                float(results_match.group(6)) * (frame_size + 20) * 8)

            moongen_results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
                (100 * float(results_match.group(6)) * 1000000) / max_theoretical_fps)

            moongen_results[ResultsConstants.TX_RATE_FPS] = (
                float(results_match.group(5)) * 1000000)

            moongen_results[ResultsConstants.TX_RATE_MBPS] = (
                float(results_match.group(5)) * (frame_size + 20) * 8)

            moongen_results[ResultsConstants.TX_RATE_PERCENT] = (
                (100 * float(results_match.group(5)) * 1000000) / max_theoretical_fps)

            moongen_results[ResultsConstants.B2B_TX_COUNT] = (
                float(results_match.group(1)))

            moongen_results[ResultsConstants.B2B_FRAMES] = (
                float(results_match.group(2)))

            moongen_results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = (
                float(results_match.group(3)))

            moongen_results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = (
                float(results_match.group(4)))

        return moongen_results

    def send_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
                                lossrate=0.0):
        #
        # Send traffic per RFC2544 throughput test specifications.
        #
        # Send packets at a variable rate, using ``traffic``
        # configuration, until minimum rate at which no packet loss is
        # detected is found.
        #
        # :param traffic: Detailed "traffic" spec, see design docs for details
        # :param tests: Number of tests to execute
        # :param duration: Per iteration duration
        # :param lossrate: Acceptable lossrate percentage
        # :returns: dictionary of strings with following data:
        #     - Tx Throughput (fps),
        #     - Rx Throughput (fps),
        #     - Tx Throughput (mbps),
        #     - Rx Throughput (mbps),
        #     - Tx Throughput (% linerate),
        #     - Rx Throughput (% linerate),
        #     - Min Latency (ns),
        #     - Max Latency (ns),
        #     - Avg Latency (ns)
        #
        self._logger.info("In moongen send_rfc2544_throughput method")
        self._params.clear()
        self._params['traffic'] = self.traffic_defaults.copy()

        if traffic:
            self._params['traffic'] = merge_spec(self._params['traffic'],
                                                 traffic)
        Moongen.create_moongen_cfg_file(self,
                                        traffic,
                                        duration=duration,
                                        acceptable_loss_pct=lossrate)

        # Initialize RFC 2544 throughput specific results
        results = OrderedDict()
        results[ResultsConstants.THROUGHPUT_RX_FPS] = 0
        results[ResultsConstants.THROUGHPUT_RX_MBPS] = 0
        results[ResultsConstants.THROUGHPUT_RX_PERCENT] = 0
        results[ResultsConstants.TX_RATE_FPS] = 0
        results[ResultsConstants.TX_RATE_MBPS] = 0
        results[ResultsConstants.TX_RATE_PERCENT] = 0
        results[ResultsConstants.MIN_LATENCY_NS] = 0
        results[ResultsConstants.MAX_LATENCY_NS] = 0
        results[ResultsConstants.AVG_LATENCY_NS] = 0

        for test_run in range(1, tests+1):
            collected_results = (
                Moongen.run_moongen_and_collect_results(self, test_run=test_run))

            results[ResultsConstants.THROUGHPUT_RX_FPS] += (
                float(collected_results[ResultsConstants.THROUGHPUT_RX_FPS]))

            results[ResultsConstants.THROUGHPUT_RX_MBPS] += (
                float(collected_results[ResultsConstants.THROUGHPUT_RX_MBPS]))

            results[ResultsConstants.THROUGHPUT_RX_PERCENT] += (
                float(collected_results[ResultsConstants.THROUGHPUT_RX_PERCENT]))

            results[ResultsConstants.TX_RATE_FPS] += (
                float(collected_results[ResultsConstants.TX_RATE_FPS]))

            results[ResultsConstants.TX_RATE_MBPS] += (
                float(collected_results[ResultsConstants.TX_RATE_MBPS]))

            results[ResultsConstants.TX_RATE_PERCENT] += (
                float(collected_results[ResultsConstants.TX_RATE_PERCENT]))

        results[ResultsConstants.THROUGHPUT_RX_FPS] = (
            '{:.6f}'.format(results[ResultsConstants.THROUGHPUT_RX_FPS] /
                            tests))

        results[ResultsConstants.THROUGHPUT_RX_MBPS] = (
            '{:.3f}'.format(results[ResultsConstants.THROUGHPUT_RX_MBPS] /
                            tests))

        results[ResultsConstants.THROUGHPUT_RX_PERCENT] = (
            '{:.3f}'.format(results[ResultsConstants.THROUGHPUT_RX_PERCENT] /
                            tests))

        results[ResultsConstants.TX_RATE_FPS] = (
            '{:.6f}'.format(results[ResultsConstants.TX_RATE_FPS] /
                            tests))

        results[ResultsConstants.TX_RATE_MBPS] = (
            '{:.3f}'.format(results[ResultsConstants.TX_RATE_MBPS] /
                            tests))

        results[ResultsConstants.TX_RATE_PERCENT] = (
            '{:.3f}'.format(results[ResultsConstants.TX_RATE_PERCENT] /
                            tests))

        results[ResultsConstants.MIN_LATENCY_NS] = (
            '{:.3f}'.format(results[ResultsConstants.MIN_LATENCY_NS] /
                            tests))

        results[ResultsConstants.MAX_LATENCY_NS] = (
            '{:.3f}'.format(results[ResultsConstants.MAX_LATENCY_NS] /
                            tests))

        results[ResultsConstants.AVG_LATENCY_NS] = (
            '{:.3f}'.format(results[ResultsConstants.AVG_LATENCY_NS] /
                            tests))

        return results

    def start_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
                                 lossrate=0.0):
        """Non-blocking version of 'send_rfc2544_throughput'.

        Start transmission and immediately return. Do not wait for
        results.
        """
        self._logger.info(
            "MOONGEN: In moongen start_rfc2544_throughput method")

    def wait_rfc2544_throughput(self):
        """Wait for and return results of RFC2544 test.
        """
        self._logger.info('In moongen wait_rfc2544_throughput')

    def send_rfc2544_back2back(self, traffic=None, tests=1, duration=60,
                               lossrate=0.0):
        """Send traffic per RFC2544 back2back test specifications.

        Send packets at a fixed rate, using ``traffic``
        configuration, for duration seconds.

        :param traffic: Detailed "traffic" spec, see design docs for details
        :param tests: Number of tests to execute
        :param duration: Per iteration duration
        :param lossrate: Acceptable loss percentage

        :returns: Named tuple of Rx Throughput (fps), Rx Throughput (mbps),
            Tx Rate (% linerate), Rx Rate (% linerate), Tx Count (frames),
            Back to Back Count (frames), Frame Loss (frames), Frame Loss (%)
        :rtype: :class:`Back2BackResult`
        """
        self._logger.info("In moongen send_rfc2544_back2back method")
        self._params.clear()
        self._params['traffic'] = self.traffic_defaults.copy()

        if traffic:
            self._params['traffic'] = merge_spec(self._params['traffic'],
                                                 traffic)

        Moongen.create_moongen_cfg_file(self,
                                        traffic,
                                        duration=duration,
                                        acceptable_loss_pct=lossrate)

        # Initialize RFC 2544 B2B specific results
        results = OrderedDict()
        results[ResultsConstants.B2B_RX_FPS] = 0
        results[ResultsConstants.B2B_TX_FPS] = 0
        results[ResultsConstants.B2B_RX_PERCENT] = 0
        results[ResultsConstants.B2B_TX_PERCENT] = 0
        results[ResultsConstants.B2B_TX_COUNT] = 0
        results[ResultsConstants.B2B_FRAMES] = 0
        results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = 0
        results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = 0
        results[ResultsConstants.SCAL_STREAM_COUNT] = 0
        results[ResultsConstants.SCAL_STREAM_TYPE] = 0
        results[ResultsConstants.SCAL_PRE_INSTALLED_FLOWS] = 0

        for test_run in range(1, tests+1):
            collected_results = (
                Moongen.run_moongen_and_collect_results(self, test_run=test_run))

            results[ResultsConstants.B2B_RX_FPS] += (
                float(collected_results[ResultsConstants.THROUGHPUT_RX_FPS]))

            results[ResultsConstants.B2B_RX_PERCENT] += (
                float(collected_results[ResultsConstants.THROUGHPUT_RX_PERCENT]))

            results[ResultsConstants.B2B_TX_FPS] += (
                float(collected_results[ResultsConstants.TX_RATE_FPS]))

            results[ResultsConstants.B2B_TX_PERCENT] += (
                float(collected_results[ResultsConstants.TX_RATE_PERCENT]))

            results[ResultsConstants.B2B_TX_COUNT] += (
                int(collected_results[ResultsConstants.B2B_TX_COUNT]))

            results[ResultsConstants.B2B_FRAMES] += (
                int(collected_results[ResultsConstants.B2B_FRAMES]))

            results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] += (
                int(collected_results[ResultsConstants.B2B_FRAME_LOSS_FRAMES]))

            results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] += (
                int(collected_results[ResultsConstants.B2B_FRAME_LOSS_PERCENT]))

        # Calculate average results
        results[ResultsConstants.B2B_RX_FPS] = (
            results[ResultsConstants.B2B_RX_FPS] / tests)

        results[ResultsConstants.B2B_RX_PERCENT] = (
            results[ResultsConstants.B2B_RX_PERCENT] / tests)

        results[ResultsConstants.B2B_TX_FPS] = (
            results[ResultsConstants.B2B_TX_FPS] / tests)

        results[ResultsConstants.B2B_TX_PERCENT] = (
            results[ResultsConstants.B2B_TX_PERCENT] / tests)

        results[ResultsConstants.B2B_TX_COUNT] = (
            results[ResultsConstants.B2B_TX_COUNT] / tests)

        results[ResultsConstants.B2B_FRAMES] = (
            results[ResultsConstants.B2B_FRAMES] / tests)

        results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = (
            results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] / tests)

        results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = (
            results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] / tests)

        results[ResultsConstants.SCAL_STREAM_COUNT] = 0
        results[ResultsConstants.SCAL_STREAM_TYPE] = 0
        results[ResultsConstants.SCAL_PRE_INSTALLED_FLOWS] = 0

        return results

    def start_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
                                lossrate=0.0):
        #
        # Non-blocking version of 'send_rfc2544_back2back'.
        #
        # Start transmission and immediately return. Do not wait for results.
        #
        self._logger.info("In Moongen start_rfc2544_back2back method")
        return NotImplementedError(
            'Moongen start back2back traffic not implemented')

    def wait_rfc2544_back2back(self):
        self._logger.info("In moongen wait_rfc2544_back2back method")
        #
        # Wait and set results of RFC2544 test.
        #
        return NotImplementedError(
            'Moongen wait back2back traffic not implemented')

if __name__ == "__main__":
    pass