From d9289a06e5cf868407eab996641f89f26a23f967 Mon Sep 17 00:00:00 2001 From: spirentbrian Date: Tue, 27 Oct 2015 14:46:33 -0400 Subject: First phase of Spirent Traffic Gen integration Change-Id: Ib852c81fc8e09593411984bfb6f9bc5d5629f0c4 --- conf/00_common.conf | 2 +- conf/10_custom.conf | 72 +++- core/traffic_controller_rfc2544.py | 10 +- docs/to-be-reorganized/quickstart.rst | 59 ++- tools/pkt_gen/testcenter/__init__.py | 18 + .../testcenter/testcenter-rfc2544-throughput.py | 418 +++++++++++++++++++++ tools/pkt_gen/testcenter/testcenter.py | 164 ++++++++ 7 files changed, 732 insertions(+), 11 deletions(-) create mode 100755 tools/pkt_gen/testcenter/__init__.py create mode 100644 tools/pkt_gen/testcenter/testcenter-rfc2544-throughput.py create mode 100644 tools/pkt_gen/testcenter/testcenter.py diff --git a/conf/00_common.conf b/conf/00_common.conf index 51dceb32..f9782c6a 100644 --- a/conf/00_common.conf +++ b/conf/00_common.conf @@ -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"] diff --git a/conf/10_custom.conf b/conf/10_custom.conf index eafa06bb..f42df2d4 100644 --- a/conf/10_custom.conf +++ b/conf/10_custom.conf @@ -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 = '' diff --git a/core/traffic_controller_rfc2544.py b/core/traffic_controller_rfc2544.py index de45de92..c3d9ab91 100644 --- a/core/traffic_controller_rfc2544.py +++ b/core/traffic_controller_rfc2544.py @@ -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. """ diff --git a/docs/to-be-reorganized/quickstart.rst b/docs/to-be-reorganized/quickstart.rst index bb009fce..bcf0f7c2 100644 --- a/docs/to-be-reorganized/quickstart.rst +++ b/docs/to-be-reorganized/quickstart.rst @@ -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 index 00000000..93eaef29 --- /dev/null +++ b/tools/pkt_gen/testcenter/__init__.py @@ -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 index 00000000..2b875c69 --- /dev/null +++ b/tools/pkt_gen/testcenter/testcenter-rfc2544-throughput.py @@ -0,0 +1,418 @@ +# 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. + +''' +@author Spirent Comunications + +This test automates the RFC2544 throughput test using the Spirent +TestCenter command sequencer. This test supports Python 2.7. + +''' +from __future__ import print_function +import argparse +import os + + +def create_dir(path): + if not os.path.exists(path): + try: + os.makedirs(path) + except Exception, e: + print("Failed to create directory %s: %s" % (path, str(e))) + raise + + +def write_query_results_to_csv(results_path, csv_results_file_prefix, query_results): + create_dir(results_path) + file = os.path.join(results_path, csv_results_file_prefix + ".csv") + with open(file, "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 destroy_session(stc, lab_server_addr, test_session_name): + stc.perform("CSServerConnectCommand", + Host=lab_server_addr) + + cs_server = stc.get("system1", "children-csserver") + + cs_test_sessions = stc.get(cs_server, "children-cstestsession").split() + + for session in cs_test_sessions: + name = stc.get(session, "Name") + if test_session_name in name: + stc.perform("CSDestroyTestSession", + testSession=session) + break + + +def create_session(stc, lab_server_addr, test_session_name): + destroy_session(stc, lab_server_addr, test_session_name) + stc.perform("CSTestSessionConnect", + Host=lab_server_addr, + CreateNewTestSession="TRUE", + TestSessionName=test_session_name) + + +def positive_int(value): + ivalue = int(value) + if ivalue <= 0: + raise argparse.ArgumentTypeError("%s is an invalid positive int value" % value) + return ivalue + + +def percent_float(value): + 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(): + parser = argparse.ArgumentParser() + # Required parameters + requirednamed = parser.add_argument_group("required named arguments") + requirednamed.add_argument("--lab_server_addr", + required=True, + help="The IP address of the Spirent Lab Server", + dest="lab_server_addr") + requirednamed.add_argument("--license_server_addr", + required=True, + help="The IP address of the Spirent License Server", + dest="license_server_addr") + requirednamed.add_argument("--east_chassis_addr", + required=True, + help="The TestCenter chassis IP address to use for the east test port", + dest="east_chassis_addr") + requirednamed.add_argument("--east_slot_num", + type=positive_int, + required=True, + help="The TestCenter slot number to use for the east test port", + dest="east_slot_num") + requirednamed.add_argument("--east_port_num", + type=positive_int, + required=True, + help="The TestCenter port number to use for the east test port", + dest="east_port_num") + requirednamed.add_argument("--west_chassis_addr", + required=True, + help="The TestCenter chassis IP address to use for the west test port", + dest="west_chassis_addr") + requirednamed.add_argument("--west_slot_num", + type=positive_int, + required=True, + help="The TestCenter slot number to use for the west test port", + dest="west_slot_num") + requirednamed.add_argument("--west_port_num", + type=positive_int, + required=True, + help="The TestCenter port number to use for the west test port", + dest="west_port_num") + # Optional parameters + optionalnamed = parser.add_argument_group("optional named arguments") + optionalnamed.add_argument("--test_session_name", + required=False, + default="RFC2544 East-West Throughput", + help="The friendly name to identify the Spirent Lab Server test session", + dest="test_session_name") + optionalnamed.add_argument("--results_dir", + required=False, + default="./Results", + help="The directory to copy results to", + dest="results_dir") + optionalnamed.add_argument("--csv_results_file_prefix", + required=False, + default="Rfc2544Tput", + help="The prefix for the CSV results files", + dest="csv_results_file_prefix") + optionalnamed.add_argument("--num_trials", + type=positive_int, + required=False, + default=1, + help="The number of trials to execute during the test", + dest="num_trials") + optionalnamed.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") + optionalnamed.add_argument("--traffic_pattern", + required=False, + choices=["BACKBONE", "MESH", "PAIR"], + default="PAIR", + help="The traffic pattern between endpoints", + dest="traffic_pattern") + optionalnamed.add_argument("--search_mode", + required=False, + choices=["COMBO", "STEP", "BINARY"], + default="BINARY", + help="The search mode used to find the throughput rate", + dest="search_mode") + optionalnamed.add_argument("--learning_mode", + required=False, + choices=["AUTO", "L2_LEARNING", "L3_LEARNING", "NONE"], + default="AUTO", + help="The learning mode used during the test, default = 'NONE'", + dest="learning_mode") + optionalnamed.add_argument("--rate_lower_limit_pct", + type=percent_float, + required=False, + default=1.0, + help="The minimum percent line rate that will be used during the test", + dest="rate_lower_limit_pct") + optionalnamed.add_argument("--rate_upper_limit_pct", + type=percent_float, + required=False, + default=99.0, + help="The maximum percent line rate that will be used during the test", + dest="rate_upper_limit_pct") + optionalnamed.add_argument("--rate_initial_pct", + type=percent_float, + required=False, + default=99.0, + help="If Search Mode is BINARY, the percent line rate that will be used at the start of the test", + dest="rate_initial_pct") + optionalnamed.add_argument("--rate_step_pct", + type=percent_float, + required=False, + default=10.0, + help="If SearchMode is STEP, the percent load increase per step", + dest="rate_step_pct") + optionalnamed.add_argument("--resolution_pct", + type=percent_float, + required=False, + default=1.0, + help="The minimum percentage of load adjustment between iterations", + dest="resolution_pct") + optionalnamed.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") + optionalnamed.add_argument("--acceptable_frame_loss_pct", + type=percent_float, + required=False, + default=0.0, + help="The maximum acceptable frame loss percent in any iteration", + dest="acceptable_frame_loss_pct") + optionalnamed.add_argument("--east_intf_addr", + required=False, + default="192.85.1.3", + help="The address to assign to the first emulated device interface on the first east port", + dest="east_intf_addr") + optionalnamed.add_argument("--east_intf_gateway_addr", + required=False, + default="192.85.1.53", + help="The gateway address to assign to the first emulated device interface on the first east port", + dest="east_intf_gateway_addr") + optionalnamed.add_argument("--west_intf_addr", + required=False, + default="192.85.1.53", + help="The address to assign to the first emulated device interface on the first west port", + dest="west_intf_addr") + optionalnamed.add_argument("--west_intf_gateway_addr", + required=False, + default="192.85.1.53", + help="The gateway address to assign to the first emulated device interface on the first west port", + dest="west_intf_gateway_addr") + parser.add_argument("-v", + "--verbose", + required=False, + default=False, + help="More output during operation when present", + action="store_true", + dest="verbose") + args = parser.parse_args() + + if args.verbose: + print("Make sure results directory exists") + create_dir(args.results_dir) + + # Load Spirent Test Center library + from StcPython import StcPython + stc = StcPython() + + tx_port_loc = "//%s/%s/%s" % (args.east_chassis_addr, + args.east_slot_num, + args.east_port_num) + rx_port_loc = "//%s/%s/%s" % (args.west_chassis_addr, + args.west_slot_num, + args.west_port_num) + + # This line will write the TestCenter commands to a log file + stc.config("automationoptions", logto="test-op", loglevel="DEBUG") + + # Retrieve and display the current API version. + if args.verbose: + print ("SpirentTestCenter system version: %s" % stc.get("system1", "version")) + + create_session(stc, args.lab_server_addr, args.test_session_name) + + try: + device_list = [] + + if args.verbose: + print("Bring up license server") + license_mgr = stc.get("system1", "children-licenseservermanager") + if args.verbose: + print("license_mgr = %s" % license_mgr) + stc.create("LicenseServer", under=license_mgr, server=args.license_server_addr) + + # Create the root project object + if args.verbose: + print ("Creating project ...") + project = stc.get("System1", "children-Project") + + # Create ports + if args.verbose: + print ("Creating ports ...") + east_chassis_port = stc.create("port", + under=project, + location=tx_port_loc, + useDefaultHost="False") + west_chassis_port = stc.create("port", + under=project, + location=rx_port_loc, + useDefaultHost="False") + + if args.verbose: + print ("Creating devices...") + # Create emulated genparam for east port + east_device_gen_params = stc.create("EmulatedDeviceGenParams", + under=project, + Port=east_chassis_port) + # Create the DeviceGenEthIIIfParams object + stc.create("DeviceGenEthIIIfParams", + under=east_device_gen_params) + # Configuring Ipv4 interfaces + stc.create("DeviceGenIpv4IfParams", + under=east_device_gen_params, + Addr=args.east_intf_addr, + Gateway=args.east_intf_gateway_addr) + # Create Devices using the Device Wizard + device_gen_config = stc.perform("DeviceGenConfigExpand", + DeleteExisting="No", + GenParams=east_device_gen_params) + # Append to the device list + device_list.append(device_gen_config['ReturnList']) + + # Create emulated genparam for west port + west_device_gen_params = stc.create("EmulatedDeviceGenParams", + under=project, + Port=west_chassis_port) + # Create the DeviceGenEthIIIfParams object + stc.create("DeviceGenEthIIIfParams", + under=west_device_gen_params) + # Configuring Ipv4 interfaces + stc.create("DeviceGenIpv4IfParams", + under=west_device_gen_params, + Addr=args.west_intf_addr, + Gateway=args.west_intf_gateway_addr) + # Create Devices using the Device Wizard + device_gen_config = stc.perform("DeviceGenConfigExpand", + DeleteExisting="No", + GenParams=west_device_gen_params) + # Append to the device list + device_list.append(device_gen_config['ReturnList']) + + if args.verbose: + print(device_list) + + # Create the RFC 2544 throughput test + if args.verbose: + print ("Set up the RFC2544 throughput test...") + stc.perform("Rfc2544SetupThroughputTestCommand", + AcceptableFrameLoss=args.acceptable_frame_loss_pct, + Duration=args.trial_duration_sec, + FrameSizeList=args.frame_size_list, + LearningMode=args.learning_mode, + NumOfTrials=args.num_trials, + RateInitial=args.rate_initial_pct, + RateLowerLimit=args.rate_lower_limit_pct, + RateStep=args.rate_step_pct, + RateUpperLimit=args.rate_upper_limit_pct, + Resolution=args.resolution_pct, + SearchMode=args.search_mode, + TrafficPattern=args.traffic_pattern) + + # Save the configuration + stc.perform("SaveToTcc", Filename="2544.tcc") + + # Connect to the hardware... + stc.perform("AttachPorts", portList=stc.get("system1.project", "children-port"), autoConnect="TRUE") + + # Apply configuration. + if args.verbose: + print("Apply configuration...") + stc.apply + + if args.verbose: + print("Starting the sequencer...") + stc.perform("SequencerStart") + + # Wait for sequencer to finish + print("Starting test... Please wait for the test to complete...") + stc.waitUntilComplete() + print("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: + print("The lab server results database is %s" % lab_server_resultsdb) + + stc.perform("CSSynchronizeFiles", + DefaultDownloadDir=args.results_dir) + + resultsdb = args.results_dir + lab_server_resultsdb.split("/Results")[1] + + print("The local summary DB file has been saved to %s" % resultsdb) + + # The returns the "RFC2544ThroughputTestResultDetailedSummaryView" table view from the results database. + # There are other views available. + resultsdict = stc.perform("QueryResult", + DatabaseConnectionString=resultsdb, + ResultPath="RFC2544ThroughputTestResultDetailedSummaryView") + if args.verbose: + print("resultsdict[\"Columns\"]: %s" % resultsdict["Columns"]) + print("resultsdict[\"Output\"]: %s" % resultsdict["Output"]) + + if args.verbose: + print("Result paths: %s" % stc.perform("GetTestResultSettingPaths")) + + # Write results to csv + if args.verbose: + print("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 Exception, e: + print("Exception: %s" % str(e)) + # destroy_session(stc, args.lab_server_addr, args.test_session_name) + raise + + if args.verbose: + print("Disconnect from lab server") + stc.perform("CSTestSessionDisconnect") + + if args.verbose: + print("Destroy session on lab server") + # destroy_session(stc, args.lab_server_addr, args.test_session_name) + print("Test complete!") + + +if __name__ == "__main__": + main() diff --git a/tools/pkt_gen/testcenter/testcenter.py b/tools/pkt_gen/testcenter/testcenter.py new file mode 100644 index 00000000..d0aac87e --- /dev/null +++ b/tools/pkt_gen/testcenter/testcenter.py @@ -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)) -- cgit 1.2.3-korg