From 1e1138cdc36ab308568e51314d967f7d13bdacc5 Mon Sep 17 00:00:00 2001
From: "Sridhar K. N. Rao" <sridhar.rao@spirent.com>
Date: Wed, 14 Sep 2016 22:42:05 +0530
Subject: pkt_gen: Spirent Testcenter RFC 2889 Support

The changes/additions, apart from spirent testcenter-specific, also
includes
to some files in conf (01_testcase and 03_traffic) and core (result-
constants, traffic_controller and component_factory) folders.

Currently, only RFC2889 Forwarding testcase is supported.

Incorporated following review suggestions:
1. Log level fixes
2. Removing unused function in results_constants.py
3. Added documentation to docs/configguide/trafficgen.rst. Userguide
   will be updated once other RFC2889 tests are implemented.
4. string matching in component_factory.
5. Remove Trailing Whitespaces

JIRA: VSPERF-286

Change-Id: I0195720ab2f8cf2c3a5aa490d66166bdca0afcb0
Signed-off-by: Sridhar K. N. Rao <sridhar.rao@spirent.com>
---
 conf/01_testcases.conf                             |   7 +
 conf/03_traffic.conf                               |   6 +
 core/component_factory.py                          |   6 +-
 core/results/results_constants.py                  |   5 +
 core/traffic_controller_rfc2889.py                 | 146 ++++++++++
 docs/configguide/trafficgen.rst                    |  36 +++
 .../pkt_gen/testcenter/testcenter-rfc2889-rest.py  | 304 +++++++++++++++++++++
 tools/pkt_gen/testcenter/testcenter.py             |  86 ++++++
 8 files changed, 595 insertions(+), 1 deletion(-)
 create mode 100644 core/traffic_controller_rfc2889.py
 create mode 100644 tools/pkt_gen/testcenter/testcenter-rfc2889-rest.py

diff --git a/conf/01_testcases.conf b/conf/01_testcases.conf
index b9c59a11..55cce1cf 100755
--- a/conf/01_testcases.conf
+++ b/conf/01_testcases.conf
@@ -131,6 +131,13 @@ PERFORMANCE_TESTS = [
         "biDirectional": "True",
         "Description": "LTD.Throughput.RFC2544.PacketLossRatio",
     },
+    {
+        "Name": "phy2phy_forwarding",
+        "Traffic Type": "rfc2889",
+        "Deployment": "p2p",
+        "biDirectional": "True",
+        "Description": "LTD.Forwarding.RFC2889.MaxForwardingRate",
+    },
     {
         "Name": "back2back",
         "Traffic Type": "back2back",
diff --git a/conf/03_traffic.conf b/conf/03_traffic.conf
index fd0f589d..04266923 100644
--- a/conf/03_traffic.conf
+++ b/conf/03_traffic.conf
@@ -75,6 +75,12 @@ TRAFFICGEN_STC_TESTCENTER_PATH = os.path.join(ROOT_DIR, 'tools/pkt_gen/testcente
 # Name of the TestCenter RFC2544 Tput helper python script
 TRAFFICGEN_STC_RFC2544_TPUT_TEST_FILE_NAME = "testcenter-rfc2544-throughput.py"
 
+# Name of the Testcenter RFC2899 Tput Helper Python Scripts
+TRAFFICGEN_STC_RFC2889_TEST_FILE_NAME = "testcenter-rfc2889-rest.py"
+
+# 2889 Port Locations
+TRAFFICGEN_STC_RFC2889_LOCATION = ""
+
 # The address of the Spirent Lab Server to use
 TRAFFICGEN_STC_LAB_SERVER_ADDR = ""
 
diff --git a/core/component_factory.py b/core/component_factory.py
index 7f453bd2..ef7ba86f 100644
--- a/core/component_factory.py
+++ b/core/component_factory.py
@@ -16,6 +16,7 @@
 """
 
 from core.traffic_controller_rfc2544 import TrafficControllerRFC2544
+from core.traffic_controller_rfc2889 import TrafficControllerRFC2889
 from core.vswitch_controller_clean import VswitchControllerClean
 from core.vswitch_controller_p2p import VswitchControllerP2P
 from core.vswitch_controller_pxp import VswitchControllerPXP
@@ -47,7 +48,10 @@ def create_traffic(traffic_type, trafficgen_class):
     :param trafficgen_class: Reference to traffic generator class to be used.
     :return: A new ITrafficController
     """
-    return TrafficControllerRFC2544(trafficgen_class)
+    if traffic_type.lower().startswith('rfc2889'):
+        return TrafficControllerRFC2889(trafficgen_class)
+    else:
+        return TrafficControllerRFC2544(trafficgen_class)
 
 
 def create_vswitch(deployment_scenario, vswitch_class, traffic,
diff --git a/core/results/results_constants.py b/core/results/results_constants.py
index b7ab7052..864712bc 100644
--- a/core/results/results_constants.py
+++ b/core/results/results_constants.py
@@ -58,6 +58,11 @@ class ResultsConstants(object):
     SCAL_STREAM_COUNT = 'stream_count'
     SCAL_STREAM_TYPE = 'match_type'
     SCAL_PRE_INSTALLED_FLOWS = 'pre-installed_flows'
+    # RFC2889 Forwarding, Address-Caching and Congestion
+    FORWARDING_RATE_FPS = 'forwarding_rate_fps'
+    ADDRS_COUNT_FLOOD_COUNT_RATIO = 'addrs_count_flood_count_ratio'
+    CONGESTION_CONTROL_EXISTS = 'congestion_control_exists'
+    PORTS_MAP = 'ports_map'
 
     TEST_RUN_TIME = "test_execution_time"
 
diff --git a/core/traffic_controller_rfc2889.py b/core/traffic_controller_rfc2889.py
new file mode 100644
index 00000000..a97a47d3
--- /dev/null
+++ b/core/traffic_controller_rfc2889.py
@@ -0,0 +1,146 @@
+"""RFC2889 Traffic Controller implementation.
+"""
+import logging
+
+from core.traffic_controller import ITrafficController
+from core.results.results_constants import ResultsConstants
+from core.results.results import IResults
+from conf import settings
+from conf import get_test_param
+
+
+class TrafficControllerRFC2889(ITrafficController, IResults):
+    """Traffic controller for RFC2889 traffic
+
+    Used to setup and control a traffic generator for an RFC2889 deployment
+    traffic scenario.
+    """
+
+    def __init__(self, traffic_gen_class):
+        """Initialise the trafficgen and store.
+
+        :param traffic_gen_class: The traffic generator class to be used.
+        """
+        self._logger = logging.getLogger(__name__)
+        self._logger.debug("__init__")
+        self._traffic_gen_class = traffic_gen_class()
+        self._traffic_started = False
+        self._traffic_started_call_count = 0
+        self._trials = int(get_test_param('rfc2889_trials', 1))
+        self._duration = int(get_test_param('duration', 30))
+        self._results = []
+
+        # If set, comma separated packet_sizes value from --test_params
+        # on cli takes precedence over value in settings file.
+        self._packet_sizes = None
+        packet_sizes_cli = get_test_param('pkt_sizes')
+        if packet_sizes_cli:
+            self._packet_sizes = [int(x.strip())
+                                  for x in packet_sizes_cli.split(',')]
+        else:
+            self._packet_sizes = settings.getValue('TRAFFICGEN_PKT_SIZES')
+
+    def __enter__(self):
+        """Call initialisation function.
+        """
+        self._traffic_gen_class.connect()
+
+    def __exit__(self, type_, value, traceback):
+        """Stop traffic, clean up.
+        """
+        if self._traffic_started:
+            self.stop_traffic()
+
+    @staticmethod
+    def _append_results(result_dict, packet_size):
+        """Adds common values to traffic generator results.
+
+        :param result_dict: Dictionary containing results from trafficgen
+        :param packet_size: Packet size value.
+
+        :returns: dictionary of results with additional entries.
+        """
+
+        ret_value = result_dict
+
+        ret_value[ResultsConstants.TYPE] = 'rfc2889'
+        ret_value[ResultsConstants.PACKET_SIZE] = str(packet_size)
+
+        return ret_value
+
+    def send_traffic(self, traffic):
+        """See ITrafficController for description
+        """
+        self._logger.debug('send_traffic with ' +
+                           str(self._traffic_gen_class))
+
+        for packet_size in self._packet_sizes:
+            # Merge framesize with the default traffic definition
+            if 'l2' in traffic:
+                traffic['l2'] = dict(traffic['l2'],
+                                     **{'framesize': packet_size})
+            else:
+                traffic['l2'] = {'framesize': packet_size}
+
+            if traffic['traffic_type'] == 'caching':
+                result = self._traffic_gen_class.send_rfc2889_caching(
+                    traffic, trials=self._trials, duration=self._duration)
+            elif traffic['traffic_type'] == 'congestion':
+                result = self._traffic_gen_class.send_rfc2889_congestion(
+                    traffic, duration=self._duration)
+            else:
+                result = self._traffic_gen_class.send_rfc2889_forwarding(
+                    traffic, tests=self._trials, duration=self._duration)
+
+            result = TrafficControllerRFC2889._append_results(result,
+                                                              packet_size)
+            self._results.append(result)
+
+    def send_traffic_async(self, traffic, function):
+        """See ITrafficController for description
+        """
+        self._logger.debug('send_traffic_async with ' +
+                           str(self._traffic_gen_class))
+
+        for packet_size in self._packet_sizes:
+            traffic['l2'] = {'framesize': packet_size}
+            self._traffic_gen_class.start_rfc2889_forwarding(
+                traffic,
+                trials=self._trials,
+                duration=self._duration)
+            self._traffic_started = True
+            if len(function['args']) > 0:
+                function['function'](function['args'])
+            else:
+                function['function']()
+            result = self._traffic_gen_class.wait_rfc2889_forwarding(
+                        traffic, trials=self._trials, duration=self._duration)
+            result = TrafficControllerRFC2889._append_results(result,
+                                                              packet_size)
+            self._results.append(result)
+
+    def stop_traffic(self):
+        """Kills traffic being sent from the traffic generator.
+        """
+        self._logger.debug("stop_traffic()")
+
+    def print_results(self):
+        """IResult interface implementation.
+        """
+        counter = 0
+        for item in self._results:
+            logging.info("Record: " + str(counter))
+            counter += 1
+            for(key, value) in list(item.items()):
+                logging.info("         Key: " + str(key) +
+                             ", Value: " + str(value))
+
+    def get_results(self):
+        """IResult interface implementation.
+        """
+        return self._results
+
+    def validate_send_traffic(self, result, traffic):
+        """Verify that send traffic has succeeded
+        """
+        return True
diff --git a/docs/configguide/trafficgen.rst b/docs/configguide/trafficgen.rst
index 28b34a6b..6ede7f2f 100644
--- a/docs/configguide/trafficgen.rst
+++ b/docs/configguide/trafficgen.rst
@@ -323,6 +323,42 @@ install the package. Once installed, the scripts named with 'rest' keyword
 can be used. For example: testcenter-rfc2544-rest.py can be used to run
 RFC 2544 tests using the REST interface.
 
+Configuration:
+~~~~~~~~~~~~~~
+The mandatory configurations are enlisted below.
+
+1. The Labserver and license server addresses. These parameters applies to
+   all the tests and are mandatory.
+
+.. code-block:: console
+
+    TRAFFICGEN_STC_LAB_SERVER_ADDR = " "
+    TRAFFICGEN_STC_LICENSE_SERVER_ADDR = " "
+
+2. For RFC2544 tests, the following parameters are mandatory
+
+
+.. code-block:: console
+
+    TRAFFICGEN_STC_RFC2544_TPUT_TEST_FILE_NAME = " "
+    TRAFFICGEN_STC_EAST_CHASSIS_ADDR = " "
+    TRAFFICGEN_STC_EAST_SLOT_NUM = " "
+    TRAFFICGEN_STC_EAST_PORT_NUM = " "
+    TRAFFICGEN_STC_EAST_INTF_ADDR = " "
+    TRAFFICGEN_STC_EAST_INTF_GATEWAY_ADDR = " "
+    TRAFFICGEN_STC_WEST_CHASSIS_ADDR = ""
+    TRAFFICGEN_STC_WEST_SLOT_NUM = " "
+    TRAFFICGEN_STC_WEST_PORT_NUM = " "
+    TRAFFICGEN_STC_WEST_INTF_ADDR = " "
+    TRAFFICGEN_STC_WEST_INTF_GATEWAY_ADDR = " "
+
+3. For RFC2889 tests, specifying the locations of the ports is mandatory.
+
+.. code-block:: console
+
+    TRAFFICGEN_STC_RFC2889_TEST_FILE_NAME = " "
+    TRAFFICGEN_STC_RFC2889_LOCATIONS= " "
+
 Xena Networks
 -------------
 
diff --git a/tools/pkt_gen/testcenter/testcenter-rfc2889-rest.py b/tools/pkt_gen/testcenter/testcenter-rfc2889-rest.py
new file mode 100644
index 00000000..cfa425e8
--- /dev/null
+++ b/tools/pkt_gen/testcenter/testcenter-rfc2889-rest.py
@@ -0,0 +1,304 @@
+# Copyright 2016 Spirent Communications.
+#
+# 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.
+
+'''
+@author Spirent Communications
+
+This test automates the RFC2544 tests using the Spirent
+TestCenter REST APIs. This test supports Python 3.4
+
+'''
+import argparse
+import logging
+import os
+
+# Logger Configuration
+logger = logging.getLogger(__name__)
+
+
+def create_dir(path):
+    """Create the directory as specified in path """
+    if not os.path.exists(path):
+        try:
+            os.makedirs(path)
+        except OSError as e:
+            logger.error("Failed to create directory %s: %s", path, str(e))
+            raise
+
+
+def write_query_results_to_csv(results_path, csv_results_file_prefix,
+                               query_results):
+    """ Write the results of the query to the CSV """
+    create_dir(results_path)
+    filec = os.path.join(results_path, csv_results_file_prefix + ".csv")
+    with open(filec, "wb") as f:
+        f.write(query_results["Columns"].replace(" ", ",") + "\n")
+        for row in (query_results["Output"].replace("} {", ",").
+                    replace("{", "").replace("}", "").split(",")):
+            f.write(row.replace(" ", ",") + "\n")
+
+
+def positive_int(value):
+    """ Positive Integer type for Arguments """
+    ivalue = int(value)
+    if ivalue <= 0:
+        raise argparse.ArgumentTypeError(
+            "%s is an invalid positive int value" % value)
+    return ivalue
+
+
+def percent_float(value):
+    """ Floating type for Arguments """
+    pvalue = float(value)
+    if pvalue < 0.0 or pvalue > 100.0:
+        raise argparse.ArgumentTypeError(
+            "%s not in range [0.0, 100.0]" % pvalue)
+    return pvalue
+
+
+def main():
+    """ Read the arguments, Invoke Test and Return the results"""
+    parser = argparse.ArgumentParser()
+    # Required parameters
+    required_named = parser.add_argument_group("required named arguments")
+    required_named.add_argument("--lab_server_addr",
+                                required=True,
+                                help=("The IP address of the "
+                                      "Spirent Lab Server"),
+                                dest="lab_server_addr")
+    required_named.add_argument("--license_server_addr",
+                                required=True,
+                                help=("The IP address of the Spirent "
+                                      "License Server"),
+                                dest="license_server_addr")
+    required_named.add_argument("--location_list",
+                                required=True,
+                                help=("A comma-delimited list of test port "
+                                      "locations"),
+                                dest="location_list")
+    # Optional parameters
+    optional_named = parser.add_argument_group("optional named arguments")
+    optional_named.add_argument("--metric",
+                                required=False,
+                                help=("One among - Forwarding,\
+                                      Address Caching and Congestion"),
+                                choices=["forwarding", "caching",
+                                         "congestion"],
+                                default="forwarding",
+                                dest="metric")
+    optional_named.add_argument("--test_session_name",
+                                required=False,
+                                default="Rfc2889Ses",
+                                help=("The friendly name to identify "
+                                      "the Spirent Lab Server test session"),
+                                dest="test_session_name")
+
+    optional_named.add_argument("--test_user_name",
+                                required=False,
+                                default="Rfc2889Usr",
+                                help=("The friendly name to identify the "
+                                      "Spirent Lab Server test user"),
+                                dest="test_user_name")
+    optional_named.add_argument("--results_dir",
+                                required=False,
+                                default="./Results",
+                                help="The directory to copy results to",
+                                dest="results_dir")
+    optional_named.add_argument("--csv_results_file_prefix",
+                                required=False,
+                                default="Rfc2889MaxFor",
+                                help="The prefix for the CSV results files",
+                                dest="csv_results_file_prefix")
+    optional_named.add_argument("--num_trials",
+                                type=positive_int,
+                                required=False,
+                                default=1,
+                                help=("The number of trials to execute during "
+                                      "the test"),
+                                dest="num_trials")
+    optional_named.add_argument("--trial_duration_sec",
+                                type=positive_int,
+                                required=False,
+                                default=60,
+                                help=("The duration of each trial executed "
+                                      "during the test"),
+                                dest="trial_duration_sec")
+    optional_named.add_argument("--traffic_pattern",
+                                required=False,
+                                choices=["BACKBONE", "MESH", "PAIR"],
+                                default="MESH",
+                                help="The traffic pattern between endpoints",
+                                dest="traffic_pattern")
+    optional_named.add_argument("--frame_size_list",
+                                type=lambda s: [int(item)
+                                                for item in s.split(',')],
+                                required=False,
+                                default=[256],
+                                help="A comma-delimited list of frame sizes",
+                                dest="frame_size_list")
+    parser.add_argument("-v",
+                        "--verbose",
+                        required=False,
+                        default=True,
+                        help="More output during operation when present",
+                        action="store_true",
+                        dest="verbose")
+    args = parser.parse_args()
+
+    if args.verbose:
+        logger.debug("Creating results directory")
+    create_dir(args.results_dir)
+    locationList = [str(item) for item in args.location_list.split(',')]
+
+    session_name = args.test_session_name
+    user_name = args.test_user_name
+
+    try:
+        # Load Spirent REST Library
+        from stcrestclient import stchttp
+
+        stc = stchttp.StcHttp(args.lab_server_addr)
+        session_id = stc.new_session(user_name, session_name)
+        stc.join_session(session_id)
+    except RuntimeError as e:
+        logger.error(e)
+        raise
+
+    # Retrieve and display the server information
+    if args.verbose:
+        logger.debug("SpirentTestCenter system version: %s",
+                     stc.get("system1", "version"))
+
+    try:
+        if args.verbose:
+            logger.debug("Bring up license server")
+        license_mgr = stc.get("system1", "children-licenseservermanager")
+        if args.verbose:
+            logger.debug("license_mgr = %s", license_mgr)
+        stc.create("LicenseServer", under=license_mgr, attributes={
+                   "server": args.license_server_addr})
+
+        # Create the root project object
+        if args.verbose:
+            logger.debug("Creating project ...")
+        project = stc.get("System1", "children-Project")
+
+        # Create ports
+        if args.verbose:
+            logger.debug("Creating ports ...")
+
+        for location in locationList:
+            stc.perform("CreateAndReservePorts", params={"locationList":
+                                                         location,
+                                                         "RevokeOwner":
+                                                         "FALSE"})
+
+        port_list_get = stc.get("System1.project", "children-port")
+
+        if args.verbose:
+            logger.debug("Adding Host Gen PArams")
+        gen_params = stc.create("EmulatedDeviceGenParams",
+                                under=project,
+                                attributes={"Port": port_list_get})
+
+        # Create the DeviceGenEthIIIfParams object
+        stc.create("DeviceGenEthIIIfParams",
+                   under=gen_params)
+        # Configuring Ipv4 interfaces
+        stc.create("DeviceGenIpv4IfParams",
+                   under=gen_params)
+
+        stc.perform("DeviceGenConfigExpand",
+                    params={"DeleteExisting": "No",
+                            "GenParams": gen_params})
+
+        if args.verbose:
+            logger.debug("Set up the RFC2889 Forwarding test...")
+        stc.perform("Rfc2889SetupMaxForwardingRateTestCommand",
+                    params={"Duration": args.trial_duration_sec,
+                            "FrameSizeList": args.frame_size_list,
+                            "NumOfTrials": args.num_trials,
+                            "TrafficPattern": args.traffic_pattern})
+
+        # Save the configuration
+        stc.perform("SaveToTcc", params={"Filename": "2889.tcc"})
+        # Connect to the hardware...
+        stc.perform("AttachPorts", params={"portList": stc.get(
+            "system1.project", "children-port"), "autoConnect": "TRUE"})
+        # Apply configuration.
+        if args.verbose:
+            logger.debug("Apply configuration...")
+        stc.apply()
+
+        if args.verbose:
+            logger.debug("Starting the sequencer...")
+        stc.perform("SequencerStart")
+
+        # Wait for sequencer to finish
+        logger.info(
+            "Starting test... Please wait for the test to complete...")
+        stc.wait_until_complete()
+        logger.info("The test has completed... Saving results...")
+
+        # Determine what the results database filename is...
+        lab_server_resultsdb = stc.get(
+            "system1.project.TestResultSetting", "CurrentResultFileName")
+
+        if args.verbose:
+            logger.debug("The lab server results database is %s",
+                         lab_server_resultsdb)
+
+        stc.perform("CSSynchronizeFiles",
+                    params={"DefaultDownloadDir": args.results_dir})
+
+        resultsdb = args.results_dir + \
+            lab_server_resultsdb.split("/Results")[1]
+
+        logger.info(
+            "The local summary DB file has been saved to %s", resultsdb)
+
+        resultsdict = (
+            stc.perform("QueryResult",
+                        params={
+                            "DatabaseConnectionString":
+                            resultsdb,
+                            "ResultPath":
+                            ("RFC2889MaxForwardingRateTestResultDetailed"
+                             "SummaryView")}))
+        if args.verbose:
+            logger.debug("resultsdict[\"Columns\"]: %s",
+                         resultsdict["Columns"])
+            logger.debug("resultsdict[\"Output\"]: %s", resultsdict["Output"])
+            logger.debug("Result paths: %s",
+                         stc.perform("GetTestResultSettingPaths"))
+
+        # Write results to csv
+        if args.verbose:
+            logger.debug("Writing CSV file to results directory %s",
+                         args.results_dir)
+        write_query_results_to_csv(
+            args.results_dir, args.csv_results_file_prefix, resultsdict)
+
+    except RuntimeError as err:
+        logger.error(err)
+
+    if args.verbose:
+        logger.debug("Destroy session on lab server")
+
+    stc.end_session()
+
+    logger.info("Test complete!")
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/pkt_gen/testcenter/testcenter.py b/tools/pkt_gen/testcenter/testcenter.py
index a6cea2e1..701d451c 100644
--- a/tools/pkt_gen/testcenter/testcenter.py
+++ b/tools/pkt_gen/testcenter/testcenter.py
@@ -115,6 +115,25 @@ def get_rfc2544_custom_settings(framesize, custom_tr, tests):
     return args
 
 
+def get_rfc2889_settings(framesize, tests, duration):
+    args = [settings.getValue("TRAFFICGEN_STC_PYTHON2_PATH"),
+            os.path.join(
+                settings.getValue("TRAFFICGEN_STC_TESTCENTER_PATH"),
+                settings.getValue(
+                    "TRAFFICGEN_STC_RFC2889_TEST_FILE_NAME")),
+                "--lab_server_addr",
+                settings.getValue("TRAFFICGEN_STC_LAB_SERVER_ADDR"),
+                "--license_server_addr",
+                settings.getValue("TRAFFICGEN_STC_LICENSE_SERVER_ADDR"),
+                "--location_list",
+                settings.getValue("TRAFFICGEN_STC_RFC2889_LOCATIONS"),
+                "--frame_size_list",
+                str(framesize),
+                "--num_trials",
+                str(tests)]
+    return args
+
+
 class TestCenter(trafficgen.ITrafficGenerator):
     """
     Spirent TestCenter
@@ -139,6 +158,48 @@ class TestCenter(trafficgen.ITrafficGenerator):
         """
         return None
 
+    def send_rfc2889_congestion(self, traffic=None, tests=1, duration=20):
+        """
+        Do nothing.
+        """
+        return None
+
+    def send_rfc2889_caching(self, traffic=None, tests=1, duration=20):
+        """
+        Do nothing.
+        """
+        return None
+
+    def get_rfc2889_results(self, filename):
+        """
+        Reads the CSV file and return the results
+        """
+        result = {}
+        with open(filename, "r") as csvfile:
+            csvreader = csv.DictReader(csvfile)
+            for row in csvreader:
+                self._logger.info("Row: %s", row)
+                duration = int((float(row["TxSignatureFrameCount"])) /
+                               (float(row["OfferedLoad(fps)"])))
+                tx_fps = (float(row["OfferedLoad(fps)"]))
+                rx_fps = float((float(row["RxFrameCount"])) /
+                               float(duration))
+                tx_mbps = ((tx_fps * float(row["FrameSize"])) /
+                           (1000000.0))
+                rx_mbps = ((rx_fps * float(row["FrameSize"])) /
+                           (1000000.0))
+                result[ResultsConstants.TX_RATE_FPS] = tx_fps
+                result[ResultsConstants.THROUGHPUT_RX_FPS] = rx_fps
+                result[ResultsConstants.TX_RATE_MBPS] = tx_mbps
+                result[ResultsConstants.THROUGHPUT_RX_MBPS] = rx_mbps
+                result[ResultsConstants.TX_RATE_PERCENT] = float(
+                    row["OfferedLoad(%)"])
+                result[ResultsConstants.FRAME_LOSS_PERCENT] = float(
+                    row["PercentFrameLoss(%)"])
+                result[ResultsConstants.FORWARDING_RATE_FPS] = float(
+                    row["ForwardingRate(fps)"])
+        return result
+
     def get_rfc2544_results(self, filename):
         """
         Reads the CSV file and return the results
@@ -211,6 +272,31 @@ class TestCenter(trafficgen.ITrafficGenerator):
 
         return self.get_rfc2544_results(filec)
 
+    def send_rfc2889_forwarding(self, traffic=None, tests=1, duration=20):
+        """
+        Send traffic per RFC2544 throughput test specifications.
+        """
+        framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE")
+        if traffic and 'l2' in traffic:
+            if 'framesize' in traffic['l2']:
+                framesize = traffic['l2']['framesize']
+        args = get_rfc2889_settings(framesize, tests, duration)
+        if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
+            args.append("--verbose")
+            verbose = True
+            self._logger.debug("Arguments used to call test: %s", args)
+        subprocess.check_call(args)
+
+        filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
+                             settings.getValue(
+                                 "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") +
+                             ".csv")
+
+        if verbose:
+            self._logger.debug("file: %s", filec)
+
+        return self.get_rfc2889_results(filec)
+
     def send_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
                                 lossrate=0.0):
         """
-- 
cgit