First phase of Spirent Traffic Gen integration 49/2849/3
authorspirentbrian <brian.castelli@spirent.com>
Tue, 27 Oct 2015 18:46:33 +0000 (14:46 -0400)
committerMaryam Tahhan <maryam.tahhan@intel.com>
Thu, 29 Oct 2015 18:26:00 +0000 (18:26 +0000)
Change-Id: Ib852c81fc8e09593411984bfb6f9bc5d5629f0c4

conf/00_common.conf
conf/10_custom.conf
core/traffic_controller_rfc2544.py
docs/to-be-reorganized/quickstart.rst
tools/pkt_gen/testcenter/__init__.py [new file with mode: 0755]
tools/pkt_gen/testcenter/testcenter-rfc2544-throughput.py [new file with mode: 0644]
tools/pkt_gen/testcenter/testcenter.py [new file with mode: 0644]

index 51dceb3..f9782c6 100644 (file)
@@ -68,4 +68,4 @@ TEST_PARAMS = {}
 # it can be used to suppress automatic load of obsoleted or abstract modules
 # Example:
 #   EXCLUDE_MODULES = ['ovs_vanilla', 'qemu_virtio_net', 'pidstat']
-EXCLUDE_MODULES = []
+EXCLUDE_MODULES = ["testcenter-rfc2544-throughput"]
index eafa06b..f42df2d 100644 (file)
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# traffic generator to use in tests
+# Operational settings for various resources
 OVS_DIR_VANILLA  = '~/src_vanilla/ovs/'
 QEMU_DIR_VANILLA = '~/src_vanilla/qemu/'
 
@@ -26,10 +26,78 @@ QEMU_DIR_USER = '~/src/qemu/'
 RTE_TARGET = '' # the relevant DPDK build target
 
 # traffic generator to use in tests
-TRAFFICGEN = 'Dummy'
+TRAFFICGEN = 'TestCenter'
+#TRAFFICGEN = 'Dummy'
 #TRAFFICGEN = 'IxNet'
 #TRAFFICGEN = 'Ixia'
 
+###########################################
+# Spirent TestCenter Configuration -- BEGIN
+# Path to Python 2 executable
+TRAFFICGEN_STC_PYTHON2_PATH = "/usr/local/bin/python2.7"
+# Path to the location of the TestCenter files
+TRAFFICGEN_STC_TESTCENTER_PATH = "./tools/pkt_gen/testcenter"
+# Name of the TestCenter RFC2544 Tput helper python script
+TRAFFICGEN_STC_RFC2544_TPUT_TEST_FILE_NAME = "testcenter-rfc2544-throughput.py"
+# The address of the Spirent Lab Server to use
+TRAFFICGEN_STC_LAB_SERVER_ADDR = "10.134.156.93"
+# The address of the Spirent License Server in your environment
+TRAFFICGEN_STC_LICENSE_SERVER_ADDR = "10.134.156.96"
+# The address of the TestCenter chassis that holds the east port
+TRAFFICGEN_STC_EAST_CHASSIS_ADDR = "10.134.156.70"
+# The slot number of the card that holds the east port
+TRAFFICGEN_STC_EAST_SLOT_NUM = "1"
+# The port number on the card that holds the east port
+TRAFFICGEN_STC_EAST_PORT_NUM = "1"
+# The address of the TestCenter chassis that holds the west port
+TRAFFICGEN_STC_WEST_CHASSIS_ADDR = "10.134.156.70"
+# The slot number of the card that holds the west port
+TRAFFICGEN_STC_WEST_SLOT_NUM = "1"
+# The port number on the card that holds the west port
+TRAFFICGEN_STC_WEST_PORT_NUM = "2"
+# The friendly name to identify the Spirent Lab Server test session
+TRAFFICGEN_STC_TEST_SESSION_NAME = "RFC2544 Tput"
+# The directory to copy results to
+TRAFFICGEN_STC_RESULTS_DIR = "./Results"
+#  The prefix for the CSV results file
+TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX = "RFC2544_tput"
+# The number of trials to execute during the test
+TRAFFICGEN_STC_NUMBER_OF_TRIALS = "1"
+# The duration of each trial executed during the test, in seconds
+TRAFFICGEN_STC_TRIAL_DURATION_SEC = "60"
+# The traffic pattern between endpoints, BACKBONE, MESH or PAIR
+TRAFFICGEN_STC_TRAFFIC_PATTERN = "PAIR"
+# The search mode used to find the throughput rate, COMBO, STEP or BINARY
+TRAFFICGEN_STC_SEARCH_MODE = "BINARY"
+# The learning mode used during the test, AUTO, L2_LEARNING, L3_LERNING, or NONE
+TRAFFICGEN_STC_LEARNING_MODE = "AUTO"
+# The minimum percent line rate that will be used during the test
+TRAFFICGEN_STC_RATE_LOWER_LIMIT_PCT = "1.0"
+# The maximum percent line rate that will be used during the test
+TRAFFICGEN_STC_RATE_UPPER_LIMIT_PCT = "99.0"
+# If SearchMode is BINARY, the percent line rate that will be used at the start of the test
+TRAFFICGEN_STC_RATE_INITIAL_PCT = "99.0"
+# When SearchMode is STEP, the percent increase in load per step
+TRAFFICGEN_STC_RATE_STEP_PCT = "10.0"
+# The minimum percentage of load adjustment between iterations
+TRAFFICGEN_STC_RESOLUTION_PCT = "1.0"
+# The frame size, in bytes
+TRAFFICGEN_STC_FRAME_SIZE = "256"
+# The maximum acceptable frame loss percent in any iteration
+TRAFFICGEN_STC_ACCEPTABLE_FRAME_LOSS_PCT = "0.0"
+# The address to assign to the first emulated device interface on the first east port
+TRAFFICGEN_STC_EAST_INTF_ADDR = "192.85.1.3"
+# The gateway address to assign to the first emulated device interface on the first east port
+TRAFFICGEN_STC_EAST_INTF_GATEWAY_ADDR = "192.85.1.103"
+# The address to assign to the first emulated device interface on the first west port
+TRAFFICGEN_STC_WEST_INTF_ADDR = "192.85.1.103"
+# The gateway address to assign to the first emulated device interface on the first west port
+TRAFFICGEN_STC_WEST_INTF_GATEWAY_ADDR = "192.85.1.3"
+# Print additional information to the terminal during the test
+TRAFFICGEN_STC_VERBOSE = "True"
+# Spirent TestCenter Configuration -- END
+#########################################
+
 # Ixia/IxNet configuration
 TRAFFICGEN_IXIA_CARD = ''
 TRAFFICGEN_IXIA_PORT1 = ''
index de45de9..c3d9ab9 100644 (file)
@@ -48,7 +48,8 @@ class TrafficControllerRFC2544(ITrafficController, IResults):
         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(',')]
+            self._packet_sizes = [int(x.strip())
+                                  for x in packet_sizes_cli.split(',')]
         else:
             self._packet_sizes = settings.getValue('TRAFFICGEN_PKT_SIZES')
 
@@ -70,13 +71,13 @@ class TrafficControllerRFC2544(ITrafficController, IResults):
         :param result_dict: Dictionary containing results from trafficgen
         :param packet_size: Packet size value.
 
-        :returns: dictionary of results with addictional entries.
+        :returns: dictionary of results with additional entries.
         """
 
         ret_value = result_dict
 
-        #TODO Old TOIT controller had knowledge about scenario beeing
-        #executed, should new controller also fill Configuration & ID,
+        # TODO Old TOIT controller had knowledge about scenario beeing
+        # executed, should new controller also fill Configuration & ID,
         # or this should be passed to TestCase?
         ret_value[ResultsConstants.TYPE] = 'rfc2544'
         ret_value[ResultsConstants.PACKET_SIZE] = str(packet_size)
@@ -151,7 +152,6 @@ class TrafficControllerRFC2544(ITrafficController, IResults):
                 logging.info("         Key: " + str(key) +
                              ", Value: " + str(value))
 
-
     def get_results(self):
         """IResult interface implementation.
         """
index bb009fc..bcf0f7c 100644 (file)
@@ -4,9 +4,13 @@ Getting Started with 'vsperf'
 Hardware Requirements
 ---------------------
 
-VSPERF requires the following hardware to run tests: IXIA traffic
-generator (IxNetwork), a machine that runs the IXIA client software and
-a CentOS Linux release 7.1.1503 (Core) host.
+VSPERF requires one of the following traffic generators to run tests:
+
+- IXIA traffic generator (IxNetwork hardware) and a machine that runs the IXIA client software
+- Spirent traffic generator (TestCenter hardware chassis or TestCenter virtual in a VM) and a
+VM to run the Spirent Virtual Deployment Service image, formerly known as "Spirent LabServer".
+
+Both test configurations, above, also require a CentOS Linux release 7.1.1503 (Core) host.
 
 vSwitch Requirements
 --------------------
@@ -41,6 +45,55 @@ need it for the 10\_custom.conf file).
 
 Hit Ok and start the TCL server application
 
+Spirent Setup
+-------------
+
+Spirent installation files and instructions are available on the
+Spirent support website at:
+
+http://support.spirent.com
+
+Select a version of Spirent TestCenter software to utilize. This example
+will use Spirent TestCenter v4.57 as an example. Substitute the appropriate
+version in place of 'v4.57' in the examples, below.
+
+On the CentOS 7 System
+~~~~~~~~~~~~~~~~~~~~~~
+
+Download and install the following:
+
+Spirent TestCenter Application, v4.57 for 64-bit Linux Client
+
+Spirent Virtual Deployment Service (VDS)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Spirent VDS is required for both TestCenter hardware and virtual
+chassis in the vsperf environment. For installation, select the version
+that matches the Spirent TestCenter Application version. For v4.57,
+the matching VDS version is 1.0.55. Download either the ova (VMware)
+or qcow2 (QEMU) image and create a VM with it. Initialize the VM
+according to Spirent installation instructions.
+
+Using Spirent TestCenter Virtual (STCv)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+STCv is available in both ova (VMware) and qcow2 (QEMU) formats. For
+VMware, download:
+
+Spirent TestCenter Virtual Machine for VMware, v4.57 for Hypervisor - VMware ESX.ESXi
+
+Virtual test port performance is affected by the hypervisor configuration. For
+best practice results in deploying STCv, the following is suggested:
+
+- Create a single VM with two test ports rather than two VMs with one port each
+- Set STCv in DPDK mode
+- Give STCv 2*n + 1 cores, where n = the number of ports. For vsperf, cores = 5.
+- Turning off hyperthreading and pinning these cores will improve performance
+- Give STCv 2 GB of RAM
+
+To get the highest performance and accuracy, Spirent TestCenter hardware is
+recommended. vsperf can run with either stype test ports.
+
 Cloning and building src dependencies
 -------------------------------------
 
diff --git a/tools/pkt_gen/testcenter/__init__.py b/tools/pkt_gen/testcenter/__init__.py
new file mode 100755 (executable)
index 0000000..93eaef2
--- /dev/null
@@ -0,0 +1,18 @@
+# Copyright 2015 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.
+
+"""Implementation of Spirent TestCenter-based vswitch performance tests.
+"""
+
+from .testcenter import *
diff --git a/tools/pkt_gen/testcenter/testcenter-rfc2544-throughput.py b/tools/pkt_gen/testcenter/testcenter-rfc2544-throughput.py
new file mode 100644 (file)
index 0000000..2b875c6
--- /dev/null
@@ -0,0 +1,418 @@
+# Copyright 2015 Spirent Communications.\r
+#\r
+# Licensed under the Apache License, Version 2.0 (the "License");\r
+# you may not use this file except in compliance with the License.\r
+# You may obtain a copy of the License at\r
+#\r
+#   http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+# Unless required by applicable law or agreed to in writing, software\r
+# distributed under the License is distributed on an "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+# See the License for the specific language governing permissions and\r
+# limitations under the License.\r
+\r
+'''\r
+@author Spirent Comunications\r
+\r
+This test automates the RFC2544 throughput test using the Spirent\r
+TestCenter command sequencer. This test supports Python 2.7.\r
+\r
+'''\r
+from __future__ import print_function\r
+import argparse\r
+import os\r
+\r
+\r
+def create_dir(path):\r
+    if not os.path.exists(path):\r
+        try:\r
+            os.makedirs(path)\r
+        except Exception, e:\r
+            print("Failed to create directory %s: %s" % (path, str(e)))\r
+            raise\r
+\r
+\r
+def write_query_results_to_csv(results_path, csv_results_file_prefix, query_results):\r
+    create_dir(results_path)\r
+    file = os.path.join(results_path, csv_results_file_prefix + ".csv")\r
+    with open(file, "wb") as f:\r
+        f.write(query_results["Columns"].replace(" ", ",") + "\n")\r
+        for row in query_results["Output"].replace("} {", ",").replace("{", "").replace("}", "").split(","):\r
+            f.write(row.replace(" ", ",") + "\n")\r
+\r
+\r
+def destroy_session(stc, lab_server_addr, test_session_name):\r
+    stc.perform("CSServerConnectCommand",\r
+                Host=lab_server_addr)\r
+\r
+    cs_server = stc.get("system1", "children-csserver")\r
+\r
+    cs_test_sessions = stc.get(cs_server, "children-cstestsession").split()\r
+\r
+    for session in cs_test_sessions:\r
+        name = stc.get(session, "Name")\r
+        if test_session_name in name:\r
+            stc.perform("CSDestroyTestSession",\r
+                        testSession=session)\r
+            break\r
+\r
+\r
+def create_session(stc, lab_server_addr, test_session_name):\r
+    destroy_session(stc, lab_server_addr, test_session_name)\r
+    stc.perform("CSTestSessionConnect",\r
+                Host=lab_server_addr,\r
+                CreateNewTestSession="TRUE",\r
+                TestSessionName=test_session_name)\r
+\r
+\r
+def positive_int(value):\r
+    ivalue = int(value)\r
+    if ivalue <= 0:\r
+        raise argparse.ArgumentTypeError("%s is an invalid positive int value" % value)\r
+    return ivalue\r
+\r
+\r
+def percent_float(value):\r
+    pvalue = float(value)\r
+    if pvalue < 0.0 or pvalue > 100.0:\r
+        raise argparse.ArgumentTypeError("%s not in range [0.0, 100.0]" % pvalue)\r
+    return pvalue\r
+\r
+\r
+def main():\r
+    parser = argparse.ArgumentParser()\r
+    # Required parameters\r
+    requirednamed = parser.add_argument_group("required named arguments")\r
+    requirednamed.add_argument("--lab_server_addr",\r
+                               required=True,\r
+                               help="The IP address of the Spirent Lab Server",\r
+                               dest="lab_server_addr")\r
+    requirednamed.add_argument("--license_server_addr",\r
+                               required=True,\r
+                               help="The IP address of the Spirent License Server",\r
+                               dest="license_server_addr")\r
+    requirednamed.add_argument("--east_chassis_addr",\r
+                               required=True,\r
+                               help="The TestCenter chassis IP address to use for the east test port",\r
+                               dest="east_chassis_addr")\r
+    requirednamed.add_argument("--east_slot_num",\r
+                               type=positive_int,\r
+                               required=True,\r
+                               help="The TestCenter slot number to use for the east test port",\r
+                               dest="east_slot_num")\r
+    requirednamed.add_argument("--east_port_num",\r
+                               type=positive_int,\r
+                               required=True,\r
+                               help="The TestCenter port number to use for the east test port",\r
+                               dest="east_port_num")\r
+    requirednamed.add_argument("--west_chassis_addr",\r
+                               required=True,\r
+                               help="The TestCenter chassis IP address to use for the west test port",\r
+                               dest="west_chassis_addr")\r
+    requirednamed.add_argument("--west_slot_num",\r
+                               type=positive_int,\r
+                               required=True,\r
+                               help="The TestCenter slot number to use for the west test port",\r
+                               dest="west_slot_num")\r
+    requirednamed.add_argument("--west_port_num",\r
+                               type=positive_int,\r
+                               required=True,\r
+                               help="The TestCenter port number to use for the west test port",\r
+                               dest="west_port_num")\r
+    # Optional parameters\r
+    optionalnamed = parser.add_argument_group("optional named arguments")\r
+    optionalnamed.add_argument("--test_session_name",\r
+                               required=False,\r
+                               default="RFC2544 East-West Throughput",\r
+                               help="The friendly name to identify the Spirent Lab Server test session",\r
+                               dest="test_session_name")\r
+    optionalnamed.add_argument("--results_dir",\r
+                               required=False,\r
+                               default="./Results",\r
+                               help="The directory to copy results to",\r
+                               dest="results_dir")\r
+    optionalnamed.add_argument("--csv_results_file_prefix",\r
+                               required=False,\r
+                               default="Rfc2544Tput",\r
+                               help="The prefix for the CSV results files",\r
+                               dest="csv_results_file_prefix")\r
+    optionalnamed.add_argument("--num_trials",\r
+                               type=positive_int,\r
+                               required=False,\r
+                               default=1,\r
+                               help="The number of trials to execute during the test",\r
+                               dest="num_trials")\r
+    optionalnamed.add_argument("--trial_duration_sec",\r
+                               type=positive_int,\r
+                               required=False,\r
+                               default=60,\r
+                               help="The duration of each trial executed during the test",\r
+                               dest="trial_duration_sec")\r
+    optionalnamed.add_argument("--traffic_pattern",\r
+                               required=False,\r
+                               choices=["BACKBONE", "MESH", "PAIR"],\r
+                               default="PAIR",\r
+                               help="The traffic pattern between endpoints",\r
+                               dest="traffic_pattern")\r
+    optionalnamed.add_argument("--search_mode",\r
+                               required=False,\r
+                               choices=["COMBO", "STEP", "BINARY"],\r
+                               default="BINARY",\r
+                               help="The search mode used to find the throughput rate",\r
+                               dest="search_mode")\r
+    optionalnamed.add_argument("--learning_mode",\r
+                               required=False,\r
+                               choices=["AUTO", "L2_LEARNING", "L3_LEARNING", "NONE"],\r
+                               default="AUTO",\r
+                               help="The learning mode used during the test, default = 'NONE'",\r
+                               dest="learning_mode")\r
+    optionalnamed.add_argument("--rate_lower_limit_pct",\r
+                               type=percent_float,\r
+                               required=False,\r
+                               default=1.0,\r
+                               help="The minimum percent line rate that will be used during the test",\r
+                               dest="rate_lower_limit_pct")\r
+    optionalnamed.add_argument("--rate_upper_limit_pct",\r
+                               type=percent_float,\r
+                               required=False,\r
+                               default=99.0,\r
+                               help="The maximum percent line rate that will be used during the test",\r
+                               dest="rate_upper_limit_pct")\r
+    optionalnamed.add_argument("--rate_initial_pct",\r
+                               type=percent_float,\r
+                               required=False,\r
+                               default=99.0,\r
+                               help="If Search Mode is BINARY, the percent line rate that will be used at the start of the test",\r
+                               dest="rate_initial_pct")\r
+    optionalnamed.add_argument("--rate_step_pct",\r
+                               type=percent_float,\r
+                               required=False,\r
+                               default=10.0,\r
+                               help="If SearchMode is STEP, the percent load increase per step",\r
+                               dest="rate_step_pct")\r
+    optionalnamed.add_argument("--resolution_pct",\r
+                               type=percent_float,\r
+                               required=False,\r
+                               default=1.0,\r
+                               help="The minimum percentage of load adjustment between iterations",\r
+                               dest="resolution_pct")\r
+    optionalnamed.add_argument("--frame_size_list",\r
+                               type=lambda s: [int(item) for item in s.split(',')],\r
+                               required=False,\r
+                               default=[256],\r
+                               help="A comma-delimited list of frame sizes",\r
+                               dest="frame_size_list")\r
+    optionalnamed.add_argument("--acceptable_frame_loss_pct",\r
+                               type=percent_float,\r
+                               required=False,\r
+                               default=0.0,\r
+                               help="The maximum acceptable frame loss percent in any iteration",\r
+                               dest="acceptable_frame_loss_pct")\r
+    optionalnamed.add_argument("--east_intf_addr",\r
+                               required=False,\r
+                               default="192.85.1.3",\r
+                               help="The address to assign to the first emulated device interface on the first east port",\r
+                               dest="east_intf_addr")\r
+    optionalnamed.add_argument("--east_intf_gateway_addr",\r
+                               required=False,\r
+                               default="192.85.1.53",\r
+                               help="The gateway address to assign to the first emulated device interface on the first east port",\r
+                               dest="east_intf_gateway_addr")\r
+    optionalnamed.add_argument("--west_intf_addr",\r
+                               required=False,\r
+                               default="192.85.1.53",\r
+                               help="The address to assign to the first emulated device interface on the first west port",\r
+                               dest="west_intf_addr")\r
+    optionalnamed.add_argument("--west_intf_gateway_addr",\r
+                               required=False,\r
+                               default="192.85.1.53",\r
+                               help="The gateway address to assign to the first emulated device interface on the first west port",\r
+                               dest="west_intf_gateway_addr")\r
+    parser.add_argument("-v",\r
+                        "--verbose",\r
+                        required=False,\r
+                        default=False,\r
+                        help="More output during operation when present",\r
+                        action="store_true",\r
+                        dest="verbose")\r
+    args = parser.parse_args()\r
+\r
+    if args.verbose:\r
+        print("Make sure results directory exists")\r
+    create_dir(args.results_dir)\r
+\r
+    # Load Spirent Test Center library\r
+    from StcPython import StcPython\r
+    stc = StcPython()\r
+\r
+    tx_port_loc = "//%s/%s/%s" % (args.east_chassis_addr,\r
+                                  args.east_slot_num,\r
+                                  args.east_port_num)\r
+    rx_port_loc = "//%s/%s/%s" % (args.west_chassis_addr,\r
+                                  args.west_slot_num,\r
+                                  args.west_port_num)\r
+\r
+    # This line will write the TestCenter commands to a log file\r
+    stc.config("automationoptions", logto="test-op", loglevel="DEBUG")\r
+\r
+    # Retrieve and display the current API version.\r
+    if args.verbose:\r
+        print ("SpirentTestCenter system version: %s" % stc.get("system1", "version"))\r
+\r
+    create_session(stc, args.lab_server_addr, args.test_session_name)\r
+\r
+    try:\r
+        device_list = []\r
+\r
+        if args.verbose:\r
+            print("Bring up license server")\r
+        license_mgr = stc.get("system1", "children-licenseservermanager")\r
+        if args.verbose:\r
+            print("license_mgr = %s" % license_mgr)\r
+        stc.create("LicenseServer", under=license_mgr, server=args.license_server_addr)\r
+\r
+        # Create the root project object\r
+        if args.verbose:\r
+            print ("Creating project ...")\r
+        project = stc.get("System1", "children-Project")\r
+\r
+        # Create ports\r
+        if args.verbose:\r
+            print ("Creating ports ...")\r
+        east_chassis_port = stc.create("port",\r
+                                       under=project,\r
+                                       location=tx_port_loc,\r
+                                       useDefaultHost="False")\r
+        west_chassis_port = stc.create("port",\r
+                                       under=project,\r
+                                       location=rx_port_loc,\r
+                                       useDefaultHost="False")\r
+\r
+        if args.verbose:\r
+            print ("Creating devices...")\r
+        # Create emulated genparam for east port\r
+        east_device_gen_params = stc.create("EmulatedDeviceGenParams",\r
+                                            under=project,\r
+                                            Port=east_chassis_port)\r
+        # Create the DeviceGenEthIIIfParams object\r
+        stc.create("DeviceGenEthIIIfParams",\r
+                   under=east_device_gen_params)\r
+        # Configuring Ipv4 interfaces\r
+        stc.create("DeviceGenIpv4IfParams",\r
+                   under=east_device_gen_params,\r
+                   Addr=args.east_intf_addr,\r
+                   Gateway=args.east_intf_gateway_addr)\r
+        # Create Devices using the Device Wizard\r
+        device_gen_config = stc.perform("DeviceGenConfigExpand",\r
+                                        DeleteExisting="No",\r
+                                        GenParams=east_device_gen_params)\r
+        # Append to the device list\r
+        device_list.append(device_gen_config['ReturnList'])\r
+\r
+        # Create emulated genparam for west port\r
+        west_device_gen_params = stc.create("EmulatedDeviceGenParams",\r
+                                            under=project,\r
+                                            Port=west_chassis_port)\r
+        # Create the DeviceGenEthIIIfParams object\r
+        stc.create("DeviceGenEthIIIfParams",\r
+                   under=west_device_gen_params)\r
+        # Configuring Ipv4 interfaces\r
+        stc.create("DeviceGenIpv4IfParams",\r
+                   under=west_device_gen_params,\r
+                   Addr=args.west_intf_addr,\r
+                   Gateway=args.west_intf_gateway_addr)\r
+        # Create Devices using the Device Wizard\r
+        device_gen_config = stc.perform("DeviceGenConfigExpand",\r
+                                        DeleteExisting="No",\r
+                                        GenParams=west_device_gen_params)\r
+        # Append to the device list\r
+        device_list.append(device_gen_config['ReturnList'])\r
+\r
+        if args.verbose:\r
+            print(device_list)\r
+\r
+        # Create the RFC 2544 throughput test\r
+        if args.verbose:\r
+            print ("Set up the RFC2544 throughput test...")\r
+        stc.perform("Rfc2544SetupThroughputTestCommand",\r
+                    AcceptableFrameLoss=args.acceptable_frame_loss_pct,\r
+                    Duration=args.trial_duration_sec,\r
+                    FrameSizeList=args.frame_size_list,\r
+                    LearningMode=args.learning_mode,\r
+                    NumOfTrials=args.num_trials,\r
+                    RateInitial=args.rate_initial_pct,\r
+                    RateLowerLimit=args.rate_lower_limit_pct,\r
+                    RateStep=args.rate_step_pct,\r
+                    RateUpperLimit=args.rate_upper_limit_pct,\r
+                    Resolution=args.resolution_pct,\r
+                    SearchMode=args.search_mode,\r
+                    TrafficPattern=args.traffic_pattern)\r
+\r
+        # Save the configuration\r
+        stc.perform("SaveToTcc", Filename="2544.tcc")\r
+\r
+        # Connect to the hardware...\r
+        stc.perform("AttachPorts", portList=stc.get("system1.project", "children-port"), autoConnect="TRUE")\r
+\r
+        # Apply configuration.\r
+        if args.verbose:\r
+            print("Apply configuration...")\r
+        stc.apply\r
+\r
+        if args.verbose:\r
+            print("Starting the sequencer...")\r
+        stc.perform("SequencerStart")\r
+\r
+        # Wait for sequencer to finish\r
+        print("Starting test... Please wait for the test to complete...")\r
+        stc.waitUntilComplete()\r
+        print("The test has completed... Saving results...")\r
+\r
+        # Determine what the results database filename is...\r
+        lab_server_resultsdb = stc.get("system1.project.TestResultSetting", "CurrentResultFileName")\r
+\r
+        if args.verbose:\r
+            print("The lab server results database is %s" % lab_server_resultsdb)\r
+\r
+        stc.perform("CSSynchronizeFiles",\r
+                    DefaultDownloadDir=args.results_dir)\r
+\r
+        resultsdb = args.results_dir + lab_server_resultsdb.split("/Results")[1]\r
+\r
+        print("The local summary DB file has been saved to %s" % resultsdb)\r
+\r
+        # The returns the "RFC2544ThroughputTestResultDetailedSummaryView" table view from the results database.\r
+        # There are other views available.\r
+        resultsdict = stc.perform("QueryResult",\r
+                                  DatabaseConnectionString=resultsdb,\r
+                                  ResultPath="RFC2544ThroughputTestResultDetailedSummaryView")\r
+        if args.verbose:\r
+            print("resultsdict[\"Columns\"]: %s" % resultsdict["Columns"])\r
+            print("resultsdict[\"Output\"]: %s" % resultsdict["Output"])\r
+\r
+        if args.verbose:\r
+            print("Result paths: %s" % stc.perform("GetTestResultSettingPaths"))\r
+\r
+        # Write results to csv\r
+        if args.verbose:\r
+            print("Writing CSV file to results directory %s" % args.results_dir)\r
+        write_query_results_to_csv(args.results_dir, args.csv_results_file_prefix, resultsdict)\r
+\r
+    except Exception, e:\r
+        print("Exception: %s" % str(e))\r
+        # destroy_session(stc, args.lab_server_addr, args.test_session_name)\r
+        raise\r
+\r
+    if args.verbose:\r
+        print("Disconnect from lab server")\r
+    stc.perform("CSTestSessionDisconnect")\r
+\r
+    if args.verbose:\r
+        print("Destroy session on lab server")\r
+    # destroy_session(stc, args.lab_server_addr, args.test_session_name)\r
+    print("Test complete!")\r
+\r
+\r
+if __name__ == "__main__":\r
+    main()\r
diff --git a/tools/pkt_gen/testcenter/testcenter.py b/tools/pkt_gen/testcenter/testcenter.py
new file mode 100644 (file)
index 0000000..d0aac87
--- /dev/null
@@ -0,0 +1,164 @@
+# Copyright 2015 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.
+
+"""
+Code to integrate Spirent TestCenter with the vsperf test framework.
+
+Provides a model for Spirent TestCenter as a test tool for implementing
+various performance tests of a virtual switch.
+"""
+
+from __future__ import print_function
+
+from tools.pkt_gen import trafficgen
+from core.results.results_constants import ResultsConstants
+import subprocess
+import os
+import csv
+from conf import settings
+
+
+class TestCenter(trafficgen.ITrafficGenerator):
+    """
+    Spirent TestCenter
+    """
+    def connect(self):
+        """
+        Do nothing.
+        """
+        return self
+
+    def disconnect(self):
+        """
+        Do nothing.
+        """
+        pass
+
+    def send_burst_traffic(self, traffic=None, numpkts=100, time=20, framerate=100):
+        """
+        Do nothing.
+        """
+        return None
+
+    def send_cont_traffic(self, traffic=None, time=20, framerate=0,
+                          multistream=False):
+        """
+        Do nothing.
+        """
+        return None
+
+    def send_rfc2544_throughput(self, traffic=None, trials=3, duration=20,
+                                lossrate=0.0, multistream=False):
+        """
+        Send traffic per RFC2544 throughput test specifications.
+        """
+        verbose = False
+
+        args = [settings.getValue("TRAFFICGEN_STC_PYTHON2_PATH"),
+                os.path.join(settings.getValue("TRAFFICGEN_STC_TESTCENTER_PATH"),
+                             settings.getValue("TRAFFICGEN_STC_RFC2544_TPUT_TEST_FILE_NAME")),
+                "--lab_server_addr",
+                settings.getValue("TRAFFICGEN_STC_LAB_SERVER_ADDR"),
+                "--license_server_addr",
+                settings.getValue("TRAFFICGEN_STC_LICENSE_SERVER_ADDR"),
+                "--east_chassis_addr",
+                settings.getValue("TRAFFICGEN_STC_EAST_CHASSIS_ADDR"),
+                "--east_slot_num",
+                settings.getValue("TRAFFICGEN_STC_EAST_SLOT_NUM"),
+                "--east_port_num",
+                settings.getValue("TRAFFICGEN_STC_EAST_PORT_NUM"),
+                "--west_chassis_addr",
+                settings.getValue("TRAFFICGEN_STC_WEST_CHASSIS_ADDR"),
+                "--west_slot_num",
+                settings.getValue("TRAFFICGEN_STC_WEST_SLOT_NUM"),
+                "--west_port_num",
+                settings.getValue("TRAFFICGEN_STC_WEST_PORT_NUM"),
+                "--test_session_name",
+                settings.getValue("TRAFFICGEN_STC_TEST_SESSION_NAME"),
+                "--results_dir",
+                settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
+                "--csv_results_file_prefix",
+                settings.getValue("TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX"),
+                "--num_trials",
+                settings.getValue("TRAFFICGEN_STC_NUMBER_OF_TRIALS"),
+                "--trial_duration_sec",
+                settings.getValue("TRAFFICGEN_STC_TRIAL_DURATION_SEC"),
+                "--traffic_pattern",
+                settings.getValue("TRAFFICGEN_STC_TRAFFIC_PATTERN"),
+                "--search_mode",
+                settings.getValue("TRAFFICGEN_STC_SEARCH_MODE"),
+                "--learning_mode",
+                settings.getValue("TRAFFICGEN_STC_LEARNING_MODE"),
+                "--rate_lower_limit_pct",
+                settings.getValue("TRAFFICGEN_STC_RATE_LOWER_LIMIT_PCT"),
+                "--rate_upper_limit_pct",
+                settings.getValue("TRAFFICGEN_STC_RATE_UPPER_LIMIT_PCT"),
+                "--rate_initial_pct",
+                settings.getValue("TRAFFICGEN_STC_RATE_INITIAL_PCT"),
+                "--rate_step_pct",
+                settings.getValue("TRAFFICGEN_STC_RATE_STEP_PCT"),
+                "--resolution_pct",
+                settings.getValue("TRAFFICGEN_STC_RESOLUTION_PCT"),
+                "--frame_size_list",
+                settings.getValue("TRAFFICGEN_STC_FRAME_SIZE"),
+                "--acceptable_frame_loss_pct",
+                settings.getValue("TRAFFICGEN_STC_ACCEPTABLE_FRAME_LOSS_PCT"),
+                "--east_intf_addr",
+                settings.getValue("TRAFFICGEN_STC_EAST_INTF_ADDR"),
+                "--east_intf_gateway_addr",
+                settings.getValue("TRAFFICGEN_STC_EAST_INTF_GATEWAY_ADDR"),
+                "--west_intf_addr",
+                settings.getValue("TRAFFICGEN_STC_WEST_INTF_ADDR"),
+                "--west_intf_gateway_addr",
+                settings.getValue("TRAFFICGEN_STC_WEST_INTF_GATEWAY_ADDR")]
+        if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
+            args.append("--verbose")
+            verbose = True
+            print("Arguments used to call test: %s" % args)
+
+        subprocess.check_call(args)
+
+        file = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"),
+                            settings.getValue("TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") + ".csv")
+        if verbose:
+            print("file: %s" % file)
+
+        result = {}
+
+        with open(file, "r") as csvfile:
+            csvreader = csv.DictReader(csvfile)
+            for row in csvreader:
+                print("Row: %s" % row)
+                result[ResultsConstants.THROUGHPUT_TX_FPS] = 0.0
+                result[ResultsConstants.THROUGHPUT_RX_FPS] = 0.0
+                result[ResultsConstants.THROUGHPUT_TX_MBPS] = 0.0
+                result[ResultsConstants.THROUGHPUT_RX_MBPS] = 0.0
+                result[ResultsConstants.THROUGHPUT_TX_PERCENT] = float(row["OfferedLoad(%)"])
+                result[ResultsConstants.THROUGHPUT_RX_PERCENT] = float(row["Throughput(%)"])
+                result[ResultsConstants.MIN_LATENCY_NS] = float(row["MinimumLatency(us)"]) * 1000
+                result[ResultsConstants.MAX_LATENCY_NS] = float(row["MaximumLatency(us)"]) * 1000
+                result[ResultsConstants.AVG_LATENCY_NS] = float(row["AverageLatency(us)"]) * 1000
+        return result
+
+if __name__ == '__main__':
+    TRAFFIC = {
+        'l3': {
+            'proto': 'tcp',
+            'srcip': '1.1.1.1',
+            'dstip': '90.90.90.90',
+        },
+    }
+
+    with TestCenter() as dev:
+        print(dev.send_rfc2544_throughput(traffic=TRAFFIC))