diff options
Diffstat (limited to 'tools/pkt_gen')
-rwxr-xr-x | tools/pkt_gen/ixnet/ixnet.py | 86 | ||||
-rw-r--r-- | tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py | 270 | ||||
-rw-r--r-- | tools/pkt_gen/testcenter/testcenter.py | 33 | ||||
-rw-r--r-- | tools/pkt_gen/trex/trex_client.py (renamed from tools/pkt_gen/trex/trex.py) | 87 |
4 files changed, 436 insertions, 40 deletions
diff --git a/tools/pkt_gen/ixnet/ixnet.py b/tools/pkt_gen/ixnet/ixnet.py index 87fb2c65..c7036606 100755 --- a/tools/pkt_gen/ixnet/ixnet.py +++ b/tools/pkt_gen/ixnet/ixnet.py @@ -83,6 +83,7 @@ import logging import os import re import csv +import random from collections import OrderedDict from tools.pkt_gen import trafficgen @@ -129,7 +130,7 @@ def _build_set_cmds(values, prefix='dict set'): if isinstance(value, list): value = '{{{}}}'.format(' '.join(str(x) for x in value)) - yield ' '.join([prefix, 'set', key, value]).strip() + yield ' '.join([prefix, key, value]).strip() continue # tcl doesn't recognise the strings "True" or "False", only "1" @@ -176,10 +177,9 @@ class IxNet(trafficgen.ITrafficGenerator): :returns: Output of command, where applicable. """ self._logger.debug('%s%s', trafficgen.CMD_PREFIX, cmd) - output = self._tclsh.eval(cmd) - return output.split() + return output def configure(self): """Configure system for IxNetwork. @@ -193,12 +193,16 @@ class IxNet(trafficgen.ITrafficGenerator): 'port': settings.getValue('TRAFFICGEN_IXNET_PORT'), 'user': settings.getValue('TRAFFICGEN_IXNET_USER'), # IXIA chassis configuration - 'chassis': settings.getValue('TRAFFICGEN_IXIA_HOST'), - 'card': settings.getValue('TRAFFICGEN_IXIA_CARD'), - 'port1': settings.getValue('TRAFFICGEN_IXIA_PORT1'), - 'port2': settings.getValue('TRAFFICGEN_IXIA_PORT2'), + 'chassis_east': settings.getValue('TRAFFICGEN_EAST_IXIA_HOST'), + 'card_east': settings.getValue('TRAFFICGEN_EAST_IXIA_CARD'), + 'port_east': settings.getValue('TRAFFICGEN_EAST_IXIA_PORT'), + 'chassis_west': settings.getValue('TRAFFICGEN_WEST_IXIA_HOST'), + 'card_west': settings.getValue('TRAFFICGEN_WEST_IXIA_CARD'), + 'port_west': settings.getValue('TRAFFICGEN_WEST_IXIA_PORT'), 'output_dir': settings.getValue('TRAFFICGEN_IXNET_TESTER_RESULT_DIR'), + 'frame_size_list': + settings.getValue('TRAFFICGEN_PKT_SIZES'), } self._logger.debug('IXIA configuration configuration : %s', self._cfg) @@ -256,11 +260,12 @@ class IxNet(trafficgen.ITrafficGenerator): 'An error occured when connecting to IxNetwork machine...') raise RuntimeError('Ixia failed to initialise.') - self.run_tcl('startRfc2544Test $config $traffic') + results_path = self.run_tcl('startRfc2544Test $config $traffic') if output: self._logger.critical( 'Failed to start continuous traffic test') raise RuntimeError('Continuous traffic test failed to start.') + return results_path def stop_cont_traffic(self): """See ITrafficGenerator for description @@ -271,9 +276,12 @@ class IxNet(trafficgen.ITrafficGenerator): lossrate=0.0): """See ITrafficGenerator for description """ - self.start_rfc2544_throughput(traffic, tests, duration, lossrate) - - return self.wait_rfc2544_throughput() + results_file = self.start_rfc2544_throughput(traffic, tests, duration, lossrate) + run_result = self.wait_rfc2544_throughput() + dest_file_name = 'Traffic_Item_Statistics_' + str(random.randrange(1, 100)) + '.csv' + self.copy_results_file(results_file, + os.path.join(settings.getValue('RESULTS_PATH'), dest_file_name)) + return run_result def start_rfc2544_throughput(self, traffic=None, tests=1, duration=20, lossrate=0.0): @@ -313,12 +321,14 @@ class IxNet(trafficgen.ITrafficGenerator): 'An error occured when connecting to IxNetwork machine...') raise RuntimeError('Ixia failed to initialise.') - self.run_tcl('startRfc2544Test $config $traffic') + results_file = self.run_tcl('startRfc2544Test $config $traffic') if output: self._logger.critical( 'Failed to start RFC2544 test') raise RuntimeError('RFC2544 test failed to start.') + return results_file + def wait_rfc2544_throughput(self): """See ITrafficGenerator for description """ @@ -397,12 +407,34 @@ class IxNet(trafficgen.ITrafficGenerator): return results output = self.run_tcl('waitForRfc2544Test') - # the run_tcl function will return a list with one element. We extract # that one element (a string representation of an IXIA-specific Tcl # datatype), parse it to find the path of the results file then parse # the results file - return parse_ixnet_rfc_results(parse_result_string(output[0])) + test_result = parse_ixnet_rfc_results(parse_result_string(output)) + return test_result + + def copy_results_file(self, source_file=None, dest_file=None): + """Copy a file from a source address to destination + """ + dest_dict = {} + source_dict = {} + srcfile = '' + if isinstance(source_file, list): + for i in source_file: + srcfile = srcfile + ' ' + i + else: + srcfile = source_file + + source = (srcfile.replace("\\", "/")).strip() + source_dict['source_file'] = {'source_file': '\"{}\"'.format(source)} + dest_dict['dest_file'] = {'dest_file': '{}'.format(dest_file)} + for cmd in _build_set_cmds(source_dict): + self.run_tcl(cmd) + for cmd in _build_set_cmds(dest_dict): + self.run_tcl(cmd) + self.run_tcl('copyFileResults $source_file $dest_file') + return dest_dict['dest_file'] def send_rfc2544_back2back(self, traffic=None, tests=1, duration=2, lossrate=0.0): @@ -411,9 +443,12 @@ class IxNet(trafficgen.ITrafficGenerator): # NOTE 2 seconds is the recommended duration for a back 2 back # test in RFC2544. 50 trials is the recommended number from the # RFC also. - self.start_rfc2544_back2back(traffic, tests, duration, lossrate) - - return self.wait_rfc2544_back2back() + b2b_results_file = self.start_rfc2544_back2back(traffic, tests, duration, lossrate) + b2b_run_result = self.wait_rfc2544_back2back() + dest_file_name = 'Traffic_Item_Statistics_' + str(random.randrange(1, 100)) + '.csv' + self.copy_results_file(b2b_results_file, + os.path.join(settings.getValue('RESULTS_PATH'), dest_file_name)) + return b2b_run_result def start_rfc2544_back2back(self, traffic=None, tests=1, duration=2, lossrate=0.0): @@ -453,15 +488,18 @@ class IxNet(trafficgen.ITrafficGenerator): 'An error occured when connecting to IxNetwork machine...') raise RuntimeError('Ixia failed to initialise.') - self.run_tcl('startRfc2544Test $config $traffic') + results_file = self.run_tcl('startRfc2544Test $config $traffic') if output: self._logger.critical( 'Failed to start RFC2544 test') raise RuntimeError('RFC2544 test failed to start.') + return results_file + def wait_rfc2544_back2back(self): """Wait for results. """ + def parse_result_string(results): """Get path to results file from output @@ -487,7 +525,7 @@ class IxNet(trafficgen.ITrafficGenerator): # transform path into something useful path = result_path.group(1).replace('\\', '/') - path = os.path.join(path, 'iteration.csv') + path = os.path.join(path, 'AggregateResults.csv') path = path.replace( settings.getValue('TRAFFICGEN_IXNET_TESTER_RESULT_DIR'), settings.getValue('TRAFFICGEN_IXNET_DUT_RESULT_DIR')) @@ -511,11 +549,11 @@ class IxNet(trafficgen.ITrafficGenerator): for row in reader: # if back2back count higher than previously found, store it # Note: row[N] here refers to the Nth column of a row - if float(row[14]) <= self._params['config']['lossrate']: - if int(row[12]) > \ + if float(row[10]) <= self._params['config']['lossrate']: + if int(float(row[8])) > \ int(results[ResultsConstants.B2B_FRAMES]): - results[ResultsConstants.B2B_FRAMES] = int(row[12]) - results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = float(row[14]) + results[ResultsConstants.B2B_FRAMES] = int(float(row[8])) + results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = float(row[10]) return results @@ -526,7 +564,7 @@ class IxNet(trafficgen.ITrafficGenerator): # datatype), parse it to find the path of the results file then parse # the results file - return parse_ixnet_rfc_results(parse_result_string(output[0])) + return parse_ixnet_rfc_results(parse_result_string(output)) def send_burst_traffic(self, traffic=None, duration=20): return NotImplementedError('IxNet does not implement send_burst_traffic') diff --git a/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py b/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py index 6c30b130..8089ef42 100644 --- a/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py +++ b/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py @@ -22,12 +22,27 @@ 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} + + +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): """Create the directory as specified in path """ @@ -39,6 +54,17 @@ def create_dir(path): 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 """ @@ -51,6 +77,46 @@ def write_query_results_to_csv(results_path, csv_results_file_prefix, 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): """ Positive Integer type for Arguments """ ivalue = int(value) @@ -68,7 +134,8 @@ def percent_float(value): "%s not in range [0.0, 100.0]" % pvalue) return pvalue -# pylint: disable=too-many-branches, too-many-statements + +# 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() @@ -146,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", @@ -269,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, @@ -309,6 +402,7 @@ def main(): _LOGGER.debug("SpirentTestCenter system version: %s", stc.get("system1", "version")) + # pylint: disable=too-many-nested-blocks try: device_list = [] port_list = [] @@ -325,6 +419,10 @@ def main(): _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: @@ -353,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, @@ -374,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, @@ -390,6 +492,45 @@ def main(): if args.verbose: _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: @@ -407,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": @@ -467,24 +609,133 @@ def main(): _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...") stc.perform("SequencerStart") - # Wait for sequencer to finish - _LOGGER.info( - "Starting test... Please wait for the test to complete...") - stc.wait_until_complete() + 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) + # 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}) @@ -565,6 +816,7 @@ def main(): args.results_dir, args.csv_results_file_prefix, resultsdict) except RuntimeError as e: + stc.end_session() _LOGGER.error(e) if args.verbose: diff --git a/tools/pkt_gen/testcenter/testcenter.py b/tools/pkt_gen/testcenter/testcenter.py index 487566bf..a15c502c 100644 --- a/tools/pkt_gen/testcenter/testcenter.py +++ b/tools/pkt_gen/testcenter/testcenter.py @@ -98,7 +98,9 @@ def get_rfc2544_common_settings(): "--trial_duration_sec", settings.getValue("TRAFFICGEN_STC_TRIAL_DURATION_SEC"), "--traffic_pattern", - settings.getValue("TRAFFICGEN_STC_TRAFFIC_PATTERN")] + settings.getValue("TRAFFICGEN_STC_TRAFFIC_PATTERN"), + "--vsperf_results_dir", + settings.getValue("RESULTS_PATH")] return args @@ -169,6 +171,7 @@ class TestCenter(trafficgen.ITrafficGenerator): Spirent TestCenter """ _logger = logging.getLogger(__name__) + _liveresults_file = settings.getValue("TRAFFICGEN_STC_LIVERESULTS_FILE") def connect(self): """ @@ -330,11 +333,13 @@ class TestCenter(trafficgen.ITrafficGenerator): return self.get_rfc2889_addr_learning_results(filec) - def get_rfc2544_results(self, filename): + def get_rfc2544_results(self, filename, genome=None): """ Reads the CSV file and return the results """ result = {} + if not os.path.exists(filename): + return result with open(filename, "r") as csvfile: csvreader = csv.DictReader(csvfile) for row in csvreader: @@ -365,6 +370,10 @@ class TestCenter(trafficgen.ITrafficGenerator): row["AverageLatency(us)"]) * 1000 result[ResultsConstants.FRAME_LOSS_PERCENT] = float( row["PercentLoss"]) + if genome: + result[ResultsConstants.IMIX_GENOME] = genome + result[ResultsConstants.IMIX_AVG_FRAMESIZE] = float( + row["AvgFrameSize"]) return result def send_cont_traffic(self, traffic=None, duration=30): @@ -419,6 +428,24 @@ class TestCenter(trafficgen.ITrafficGenerator): tests) args = rfc2544_common_args + stc_common_args + rfc2544_custom_args + if traffic and 'latency_histogram' in traffic: + if traffic['latency_histogram']['enabled']: + if traffic['latency_histogram']['type'] == 'Default': + args.append("--latency_histogram") + + genome = '' + if traffic and 'imix' in traffic: + if traffic['imix']['enabled']: + if traffic['imix']['type'] == 'genome': + genome = traffic['imix']['genome'] + args.append('--imix') + args.append(genome) + + if settings.getValue("TRAFFICGEN_STC_LIVE_RESULTS") == "True": + args.append('--live_results') + args.append('--logfile') + args.append(self._liveresults_file) + if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True": args.append("--verbose") verbose = True @@ -433,7 +460,7 @@ class TestCenter(trafficgen.ITrafficGenerator): if verbose: self._logger.info("file: %s", filec) - return self.get_rfc2544_results(filec) + return self.get_rfc2544_results(filec, genome) def send_rfc2544_back2back(self, traffic=None, tests=1, duration=20, lossrate=0.0): diff --git a/tools/pkt_gen/trex/trex.py b/tools/pkt_gen/trex/trex_client.py index 94b793d6..3d6836d8 100644 --- a/tools/pkt_gen/trex/trex.py +++ b/tools/pkt_gen/trex/trex_client.py @@ -26,7 +26,7 @@ import re from collections import OrderedDict # pylint: disable=unused-import import netaddr -import zmq +#import zmq from conf import settings from conf import merge_spec from core.results.results_constants import ResultsConstants @@ -35,7 +35,7 @@ try: # pylint: disable=wrong-import-position, import-error sys.path.append(settings.getValue('PATHS')['trafficgen']['Trex']['src']['path']) from trex_stl_lib.api import * - from trex_stl_lib import trex_stl_exceptions + # from trex_stl_lib import trex_stl_exceptions except ImportError: # VSPERF performs detection of T-Rex api during testcase initialization. So if # T-Rex is requsted and API is not available it will fail before this code @@ -160,7 +160,7 @@ class Trex(ITrafficGenerator): try: self._stlclient = STLClient(username=self._trex_user, server=self._trex_host_ip_addr, - verbose_level=0) + verbose_level='info') self._stlclient.connect() except STLError: raise RuntimeError('T-Rex: Cannot connect to T-Rex server. Please check if it is ' @@ -351,6 +351,8 @@ class Trex(ITrafficGenerator): return (stream_1, stream_2, stream_1_lat, stream_2_lat) + + # pylint: disable=too-many-locals, too-many-statements def generate_traffic(self, traffic, duration, disable_capture=False): """The method that generate a stream """ @@ -414,7 +416,70 @@ class Trex(ITrafficGenerator): core_mask=self._stlclient.CORE_MASK_PIN) except STLError: self._stlclient.start(ports=my_ports, force=True, duration=duration, mult="{}gbps".format(gbps_speed)) - self._stlclient.wait_on_traffic(ports=my_ports) + + if settings.getValue('TRAFFICGEN_TREX_LIVE_RESULTS'): + filec = os.path.join(settings.getValue('RESULTS_PATH'), + settings.getValue('TRAFFICGEN_TREX_LC_FILE')) + filee = os.path.join(settings.getValue('RESULTS_PATH'), + settings.getValue('TRAFFICGEN_TREX_LE_FILE')) + pgids = self._stlclient.get_active_pgids() + rx_port_0 = 1 + tx_port_0 = 0 + rx_port_1 = 0 + tx_port_1 = 1 + with open(filec, 'a') as fcp, open(filee, 'a') as fep: + fcp.write("ts,rx_port,tx_port,rx_pkts,tx_pkts,rx_pps,tx_pps,"+ + "rx_bps_num,rx_bps_den,tx_bps_num,tx_bps_den\n") + fep.write('ts,dropped,ooo,dup,seq_too_high,seq_too_low\n') + while True: + tr_status = self._stlclient.is_traffic_active(ports=my_ports) + if not tr_status: + break + time.sleep(1) + stats = self._stlclient.get_pgid_stats(pgids['flow_stats']) + lat_stats = stats['latency'].get(0) + flow_stats_0 = stats['flow_stats'].get(0) + flow_stats_1 = stats['flow_stats'].get(1) + if flow_stats_0: + rx_pkts = flow_stats_0['rx_pkts'][rx_port_0] + tx_pkts = flow_stats_0['tx_pkts'][tx_port_0] + rx_pps = flow_stats_0['rx_pps'][rx_port_0] + tx_pps = flow_stats_0['tx_pps'][tx_port_0] + rx_bps = flow_stats_0['rx_bps'][rx_port_0] + tx_bps = flow_stats_0['tx_bps'][tx_port_0] + rx_bps_l1 = flow_stats_0['rx_bps_l1'][rx_port_0] + tx_bps_l1 = flow_stats_0['tx_bps_l1'][tx_port_0] + # https://github.com/cisco-system-traffic-generator/\ + # trex-core/blob/master/scripts/automation/\ + # trex_control_plane/interactive/trex/examples/\ + # stl/stl_flow_latency_stats.py + fcp.write("{10},{8},{9},{0},{1},{2},{3},{4},{5},{6},{7}\n" + .format(rx_pkts, tx_pkts, rx_pps, tx_pps, + rx_bps, rx_bps_l1, tx_bps, tx_bps_l1, + rx_port_0, tx_port_0, time.time())) + if flow_stats_1: + rx_pkts = flow_stats_1['rx_pkts'][rx_port_1] + tx_pkts = flow_stats_1['tx_pkts'][tx_port_1] + rx_pps = flow_stats_1['rx_pps'][rx_port_1] + tx_pps = flow_stats_1['tx_pps'][tx_port_1] + rx_bps = flow_stats_1['rx_bps'][rx_port_1] + tx_bps = flow_stats_1['tx_bps'][tx_port_1] + rx_bps_l1 = flow_stats_1['rx_bps_l1'][rx_port_1] + tx_bps_l1 = flow_stats_1['tx_bps_l1'][tx_port_1] + fcp.write("{10},{8},{9},{0},{1},{2},{3},{4},{5},{6},{7}\n" + .format(rx_pkts, tx_pkts, rx_pps, tx_pps, + rx_bps, rx_bps_l1, tx_bps, tx_bps_l1, + rx_port_1, tx_port_1, time.time())) + if lat_stats: + drops = lat_stats['err_cntrs']['dropped'] + ooo = lat_stats['err_cntrs']['out_of_order'] + dup = lat_stats['err_cntrs']['dup'] + sth = lat_stats['err_cntrs']['seq_too_high'] + stl = lat_stats['err_cntrs']['seq_too_low'] + fep.write('{5},{0},{1},{2},{3},{4}\n' + .format(drops, ooo, dup, sth, stl, time.time())) + else: + self._stlclient.wait_on_traffic(ports=my_ports) stats = self._stlclient.get_stats(sync_now=True) # export captured data into pcap file if possible @@ -529,9 +594,14 @@ class Trex(ITrafficGenerator): :return: passing stats as dictionary """ threshold = settings.getValue('TRAFFICGEN_TREX_RFC2544_TPUT_THRESHOLD') + max_repeat = settings.getValue('TRAFFICGEN_TREX_RFC2544_MAX_REPEAT') + loss_verification = settings.getValue('TRAFFICGEN_TREX_RFC2544_BINARY_SEARCH_LOSS_VERIFICATION') + if loss_verification: + self._logger.info("Running Binary Search with Loss Verification") stats_ok = _EMPTY_STATS new_params = copy.deepcopy(traffic) iteration = 1 + repeat = 0 left = boundaries['left'] right = boundaries['right'] center = boundaries['center'] @@ -555,11 +625,20 @@ class Trex(ITrafficGenerator): if test_lossrate == 0.0 and new_params['frame_rate'] == traffic['frame_rate']: return copy.deepcopy(stats) elif test_lossrate > lossrate: + if loss_verification: + if repeat < max_repeat: + repeat += 1 + iteration += 1 + continue + else: + repeat = 0 right = center center = (left + right) / 2 new_params = copy.deepcopy(traffic) new_params['frame_rate'] = center else: + if loss_verification: + repeat = 0 stats_ok = copy.deepcopy(stats) left = center center = (left + right) / 2 |