X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=tools%2Fpkt_gen%2Ftestcenter%2Ftestcenter-rfc2544-rest.py;h=8089ef4254ddfa10ac1ad0fc01c4ed0aebb33d3e;hb=HEAD;hp=91f7e27f3b815dd050cdd21368b1a5e6dd874e33;hpb=dd47e4e2ea55f01235c20bed8e07d69fb51c292e;p=vswitchperf.git diff --git a/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py b/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py index 91f7e27f..8089ef42 100644 --- a/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py +++ b/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py @@ -1,4 +1,4 @@ -# Copyright 2016 Spirent Communications. +# Copyright 2016-2017 Spirent Communications. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,7 +11,9 @@ # 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. - +# +# Invalid name of file, must be used '_' instead '-' +# pylint: disable=invalid-name ''' @author Spirent Communications @@ -20,11 +22,26 @@ TestCenter REST APIs. This test supports Python 3.4 ''' import argparse +import collections import logging import os +import sqlite3 +import time + +_LOGGER = logging.getLogger(__name__) + +GENOME_PKTSIZE_ENCODING = {"a": 64, "b": 128, "c": 256, "d": 512, + "e": 1024, "f": 1280, "g": 1518, "h": 2112} -logger = logging.getLogger(__name__) +def genome2weights(sequence): + """ Convert genome sequence to packetsize weights""" + weights = collections.defaultdict(int) + for char in GENOME_PKTSIZE_ENCODING: + charcount = sequence.count(char) + if charcount: + weights[GENOME_PKTSIZE_ENCODING[char]] = charcount + return weights def create_dir(path): @@ -32,21 +49,72 @@ def create_dir(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)) + except OSError as ex: + _LOGGER.error("Failed to create directory %s: %s", path, str(ex)) raise +def write_histogram_to_csv(results_path, csv_results_file_prefix, + counts, ranges): + """ Write the results of the query to the CSV """ + filec = os.path.join(results_path, csv_results_file_prefix + ".csv") + with open(filec, "wb") as result_file: + for key in counts: + result_file.write(str(key) + "\n") + result_file.write(str(ranges) + "\n") + result_file.write(str(counts[key]) + "\n") + + 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") + with open(filec, "wb") as result_file: + result_file.write(query_results["Columns"].replace(" ", ",") + "\n") for row in (query_results["Output"].replace("} {", ","). replace("{", "").replace("}", "").split(",")): - f.write(row.replace(" ", ",") + "\n") + result_file.write(row.replace(" ", ",") + "\n") + + +def write_headers(results_path, file_name, rx_tx): + """ Write headers for the live-results files """ + filec = os.path.join(results_path, file_name + rx_tx) + with open(filec, "a") as result_file: + if 'rx' in rx_tx: + result_file.write('Time,RxPrt,DrpFrCnt,SeqRnLen,AvgLat,' + + 'DrpFrRate,FrCnt,FrRate,MaxLat,MinLat,' + + 'OctCnt,OctRate\n') + else: + result_file.write('Time,StrId,BlkId,FrCnt,FrRate,ERxFrCnt,' + + 'OctCnt,OctRate,bitCnt,bitRate\n') + + +def write_rx_live_results_to_file(results_path, file_name, results): + """ Write live results from the rx-ports""" + filec = os.path.join(results_path, file_name + ".rx") + with open(filec, "a") as result_file: + result_file.write('{0},{3},{1},{2},{4},{5},{6},{7},{8},{9},{10},{11}\n' + .format(time.time(), results['DroppedFrameCount'], + results['SeqRunLength'], results['RxPort'], + results['AvgLatency'], + results['DroppedFrameRate'], + results['FrameCount'], results['FrameRate'], + results['MaxLatency'], results['MinLatency'], + results['OctetCount'], results['OctetRate'])) + + +def write_tx_live_results_to_file(results_path, file_name, results): + """ Write live results from the tx-ports""" + filec = os.path.join(results_path, file_name + ".tx") + with open(filec, "a") as result_file: + result_file.write('{0},{1},{9},{2},{3},{4},{5},{6},{7},{8}\n' + .format(time.time(), results['StreamId'], + results['FrameCount'], results['FrameRate'], + results['ExpectedRxFrameCount'], + results['OctetCount'], results['OctetRate'], + results['BitCount'], results['BitRate'], + results['BlockId'])) def positive_int(value): @@ -67,6 +135,7 @@ def percent_float(value): return pvalue +# pylint: disable=too-many-branches, too-many-statements, too-many-locals def main(): """ Read the arguments, Invoke Test and Return the results""" parser = argparse.ArgumentParser() @@ -144,6 +213,11 @@ def main(): default="./Results", help="The directory to copy results to", dest="results_dir") + optional_named.add_argument("--vsperf_results_dir", + required=False, + default="./Results", + help="The directory to copy results to", + dest="vsperf_results_dir") optional_named.add_argument("--csv_results_file_prefix", required=False, default="Rfc2544Tput", @@ -267,6 +341,27 @@ def main(): "the first emulated device interface" "on the first west port"), dest="west_intf_gateway_addr") + optional_named.add_argument("--latency_histogram", + required=False, + action="store_true", + help="latency histogram is required in output?", + dest="latency_histogram") + optional_named.add_argument("--imix", + required=False, + default="", + help=("IMIX specification as genome" + "Encoding - RFC 6985"), + dest="imix") + optional_named.add_argument("--live_results", + required=False, + action="store_true", + help="Live Results required?", + dest="live_results") + optional_named.add_argument("--logfile", + required=False, + default="./traffic_gen.log", + help="Log file to log live results", + dest="logfile") parser.add_argument("-v", "--verbose", required=False, @@ -277,12 +372,12 @@ def main(): args = parser.parse_args() if args.verbose: - logger.debug("Creating results directory") + _LOGGER.debug("Creating results directory") create_dir(args.results_dir) session_name = args.test_session_name user_name = args.test_user_name - + # pylint: disable=import-error try: # Load Spirent REST Library from stcrestclient import stchttp @@ -290,8 +385,8 @@ def main(): 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) + except RuntimeError as err: + _LOGGER.error(err) raise # Get STC system info. @@ -304,43 +399,48 @@ def main(): # Retrieve and display the server information if args.verbose: - logger.debug("SpirentTestCenter system version: %s", - stc.get("system1", "version")) + _LOGGER.debug("SpirentTestCenter system version: %s", + stc.get("system1", "version")) + # pylint: disable=too-many-nested-blocks try: device_list = [] port_list = [] if args.verbose: - logger.debug("Bring up license server") + _LOGGER.debug("Bring up license server") license_mgr = stc.get("system1", "children-licenseservermanager") if args.verbose: - logger.debug("license_mgr = %s", license_mgr) + _LOGGER.debug("license_mgr = %s", license_mgr) stc.create("LicenseServer", under=license_mgr, attributes={ - "server": args.license_server_addr}) + "server": args.license_server_addr}) # Create the root project object if args.verbose: - logger.debug("Creating project ...") + _LOGGER.debug("Creating project ...") project = stc.get("System1", "children-Project") + # Configure the Result view + resultopts = stc.get('project1', 'children-resultoptions') + stc.config(resultopts, {'ResultViewMode': 'BASIC'}) + # Configure any custom traffic parameters if args.traffic_custom == "cont": if args.verbose: - logger.debug("Configure Continuous Traffic") + _LOGGER.debug("Configure Continuous Traffic") stc.create("ContinuousTestConfig", under=project) # Create ports if args.verbose: - logger.debug("Creating ports ...") + _LOGGER.debug("Creating ports ...") east_chassis_port = stc.create('port', project) if args.verbose: - logger.debug("Configuring TX port ...") + _LOGGER.debug("Configuring TX port ...") stc.config(east_chassis_port, {'location': tx_port_loc}) port_list.append(east_chassis_port) west_chassis_port = stc.create('port', project) if args.verbose: - logger.debug("Configuring RX port ...") + _LOGGER.debug("Configuring RX port ...") stc.config(west_chassis_port, {'location': rx_port_loc}) port_list.append(west_chassis_port) @@ -351,7 +451,9 @@ def main(): east_chassis_port}) # Create the DeviceGenEthIIIfParams object stc.create("DeviceGenEthIIIfParams", - under=east_device_gen_params) + under=east_device_gen_params, + attributes={'UseDefaultPhyMac': True}) + # Configuring Ipv4 interfaces stc.create("DeviceGenIpv4IfParams", under=east_device_gen_params, @@ -372,7 +474,9 @@ def main(): west_chassis_port}) # Create the DeviceGenEthIIIfParams object stc.create("DeviceGenEthIIIfParams", - under=west_device_gen_params) + under=west_device_gen_params, + attributes={'UseDefaultPhyMac': True}) + # Configuring Ipv4 interfaces stc.create("DeviceGenIpv4IfParams", under=west_device_gen_params, @@ -386,12 +490,51 @@ def main(): # Append to the device list device_list.append(device_gen_config['ReturnList']) if args.verbose: - logger.debug(device_list) + _LOGGER.debug(device_list) + + # Configure Histogram + if args.latency_histogram: + # Generic Configuration + histResOptions = stc.get("project1", 'children-ResultOptions') + stc.config(histResOptions, {'ResultViewMode': 'HISTOGRAM'}) + # East Port Configuration + histAnaEast = stc.get(east_chassis_port, 'children-Analyzer') + histAnaEastConfig = stc.get(histAnaEast, 'children-AnalyzerConfig') + stc.config(histAnaEastConfig, {'HistogramMode': 'LATENCY'}) + eLatHist = stc.get(histAnaEastConfig, 'children-LatencyHistogram') + stc.config(eLatHist, {'ConfigMode': 'CONFIG_LIMIT_MODE', + 'BucketSizeUnit': 'ten_nanoseconds', + 'Active': 'TRUE', + 'DistributionMode': 'CENTERED_MODE'}) + # West Port Configuration + histAnaWest = stc.get(west_chassis_port, 'children-Analyzer') + histAnaWestConfig = stc.get(histAnaWest, 'children-AnalyzerConfig') + stc.config(histAnaWestConfig, {'HistogramMode': 'LATENCY'}) + wLatHist = stc.get(histAnaWestConfig, 'children-LatencyHistogram') + stc.config(wLatHist, {'ConfigMode': 'CONFIG_LIMIT_MODE', + 'BucketSizeUnit': 'ten_nanoseconds', + 'Active': 'TRUE', + 'DistributionMode': 'CENTERED_MODE'}) + gBucketSizeList = stc.get(wLatHist, 'BucketSizeList') + # gLimitSizeList = stc.get(wLatHist, 'LimitList') + + # IMIX configuration + fld = None + if args.imix: + args.frame_size_list = [] + weights = genome2weights(args.imix) + fld = stc.create('FrameLengthDistribution', under=project) + def_slots = stc.get(fld, "children-framelengthdistributionslot") + stc.perform("Delete", params={"ConfigList": def_slots}) + for fsize in weights: + stc.create('framelengthdistributionslot', under=fld, + attributes={'FixedFrameLength': fsize, + 'Weight': weights[fsize]}) # Create the RFC 2544 'metric test if args.metric == "throughput": if args.verbose: - logger.debug("Set up the RFC2544 throughput test...") + _LOGGER.debug("Set up the RFC2544 throughput test...") stc.perform("Rfc2544SetupThroughputTestCommand", params={"AcceptableFrameLoss": args.acceptable_frame_loss_pct, @@ -405,7 +548,8 @@ def main(): "RateUpperLimit": args.rate_upper_limit_pct, "Resolution": args.resolution_pct, "SearchMode": args.search_mode, - "TrafficPattern": args.traffic_pattern}) + "TrafficPattern": args.traffic_pattern, + "FrameSizeDistributionList": fld}) elif args.metric == "backtoback": stc.perform("Rfc2544SetupBackToBackTestCommand", params={"AcceptableFrameLoss": @@ -462,26 +606,135 @@ def main(): "system1.project", "children-port"), "autoConnect": "TRUE"}) # Apply configuration. if args.verbose: - logger.debug("Apply configuration...") + _LOGGER.debug("Apply configuration...") stc.apply() + # Register for the results + hResDataRx = stc.create('ResultDataSet', under='project1') + strmBlockList = stc.get('project1', 'children-streamblock') + stc.create('ResultQuery', under=hResDataRx, attributes={ + 'ResultRootList': strmBlockList, + 'ConfigClassId': 'StreamBlock', + 'ResultClassId': 'RxStreamSummaryResults', + 'PropertyIdArray': "RxStreamSummaryResults.RxPort \ + RxStreamSummaryResults.AvgLatency \ + RxStreamSummaryResults.BitCount \ + RxStreamSummaryResults.BitRate \ + RxStreamSummaryResults.DroppedFrameCount\ + RxStreamSummaryResults.DroppedFrameRate \ + RxStreamSummaryResults.FrameCount \ + RxStreamSummaryResults.FrameRate \ + RxStreamSummaryResults.MaxLatency \ + RxStreamSummaryResults.MinLatency \ + RxStreamSummaryResults.OctetCount \ + RxStreamSummaryResults.OctetRate \ + RxStreamSummaryResults.SeqRunLength"}) + hResDataTx = stc.create('ResultDataSet', under='project1') + strmBlockList = stc.get('project1', 'children-streamblock') + stc.create('ResultQuery', under=hResDataTx, attributes={ + 'ResultRootList': strmBlockList, + 'ConfigClassId': 'StreamBlock', + 'ResultClassId': 'TxStreamResults', + 'PropertyIdArray': "TxStreamResults.BlockId \ + TxStreamResults.BitCount \ + TxStreamResults.BitRate \ + TxStreamResults.FrameCount \ + TxStreamResults.FrameRate \ + TxStreamResults.OctetCount \ + TxStreamResults.OctetRate"}) + stc.perform('ResultDataSetSubscribe', params={'ResultDataSet': hResDataRx}) + stc.perform('ResultDataSetSubscribe', params={'ResultDataSet': hResDataTx}) + time.sleep(3) + stc.perform('RefreshResultView', params={'ResultDataSet': hResDataTx}) + hndListRx = stc.get(hResDataRx, 'ResultHandleList') + hndListTx = stc.get(hResDataTx, 'ResultHandleList') + if args.verbose: - logger.debug("Starting the sequencer...") + _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...") + sequencer = stc.get("system1", "children-sequencer") + state = stc.get(sequencer, 'State') + + # If Live-results are required, we don't wait for the test to complete + if args.live_results: + write_headers(args.vsperf_results_dir, args.logfile, '.rx') + write_headers(args.vsperf_results_dir, args.logfile, '.tx') + while state != 'IDLE': + state = stc.get(sequencer, 'State') + hndListTx = stc.get(hResDataTx, 'ResultHandleList') + if hndListTx: + handles = hndListTx.split(' ') + for handle in handles: + tx_values = stc.get(handle) + write_tx_live_results_to_file(args.vsperf_results_dir, + args.logfile, + tx_values) + if hndListRx: + handles = hndListRx.split(' ') + for handle in handles: + rx_values = stc.get(handle) + write_rx_live_results_to_file(args.vsperf_results_dir, + args.logfile, + rx_values) + time.sleep(1) + # Live results not needed, so just wait! + else: + # 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 not lab_server_resultsdb or 'Results' not in lab_server_resultsdb: + _LOGGER.info("Failed to find results.") + stc.end_session() + return + if args.verbose: - logger.debug("The lab server results database is %s", - lab_server_resultsdb) + _LOGGER.debug("The lab server results database is %s", + lab_server_resultsdb) + + # Create Latency Histogram CSV file() + if args.latency_histogram: + hist_dict_counts = {} + for file_url in stc.files(): + if '-FrameSize-' in file_url: + stc.download(file_url) + filename = file_url.split('/')[-1] + if os.path.exists(os.getcwd() + '/' + filename): + conn = sqlite3.connect(os.getcwd() + '/' + filename) + # cursor = conn.execute( + # 'select * from RxEotStreamResults') + # names = [desc[0] for desc in cursor.description] + counts = conn.execute("SELECT \ + HistBin1Count, HistBin2Count,\ + HistBin3Count, HistBin4Count,\ + HistBin5Count, HistBin6Count,\ + HistBin7Count, HistBin8Count,\ + HistBin9Count, HistBin10Count,\ + HistBin11Count, HistBin12Count,\ + HistBin13Count, HistBin14Count, \ + HistBin15Count, HistBin16Count \ + from RxEotStreamResults") + strs = filename.split('-') + key = strs[strs.index('FrameSize')+1] + if key in hist_dict_counts: + hist_dict_counts[key] = [a+b for a, b in + zip(counts.fetchone(), + hist_dict_counts[key])] + else: + hist_dict_counts[key] = counts.fetchone() + conn.close() + + write_histogram_to_csv(args.vsperf_results_dir, 'Histogram', + hist_dict_counts, + gBucketSizeList) stc.perform("CSSynchronizeFiles", params={"DefaultDownloadDir": args.results_dir}) @@ -489,8 +742,13 @@ def main(): resultsdb = args.results_dir + \ lab_server_resultsdb.split("/Results")[1] - logger.info( - "The local summary DB file has been saved to %s", resultsdb) + if not os.path.exists(resultsdb): + resultsdb = lab_server_resultsdb + _LOGGER.info("Failed to create the local summary DB File, using" + " the remote DB file instead.") + else: + _LOGGER.info( + "The local summary DB file has been saved to %s", resultsdb) # The returns the "RFC2544ThroughputTestResultDetailedSummaryView" # table view from the results database. @@ -545,26 +803,27 @@ def main(): ("RFC2544FrameLossTestResultDetailed" "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")) + _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 - logger.debug("Writing CSV file to results directory %s", - args.results_dir) + _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 e: - logger.error(e) + stc.end_session() + _LOGGER.error(e) if args.verbose: - logger.debug("Destroy session on lab server") + _LOGGER.debug("Destroy session on lab server") stc.end_session() - logger.info("Test complete!") + _LOGGER.info("Test complete!") if __name__ == "__main__": main()