diff options
Diffstat (limited to 'tools/pkt_gen')
-rw-r--r-- | tools/pkt_gen/moongen/__init__.py | 13 | ||||
-rw-r--r-- | tools/pkt_gen/moongen/moongen.py | 753 | ||||
-rw-r--r-- | tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py | 11 | ||||
-rw-r--r-- | tools/pkt_gen/testcenter/testcenter.py | 120 | ||||
-rw-r--r-- | tools/pkt_gen/xena/profiles/baseconfig.x2544 | 2 | ||||
-rwxr-xr-x | tools/pkt_gen/xena/xena.py | 57 | ||||
-rw-r--r-- | tools/pkt_gen/xena/xena_json.py | 39 |
7 files changed, 960 insertions, 35 deletions
diff --git a/tools/pkt_gen/moongen/__init__.py b/tools/pkt_gen/moongen/__init__.py new file mode 100644 index 00000000..562eb088 --- /dev/null +++ b/tools/pkt_gen/moongen/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 Intel Corporation. +# +# 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. diff --git a/tools/pkt_gen/moongen/moongen.py b/tools/pkt_gen/moongen/moongen.py new file mode 100644 index 00000000..d6c09e5d --- /dev/null +++ b/tools/pkt_gen/moongen/moongen.py @@ -0,0 +1,753 @@ +# Copyright 2016 Red Hat Inc +# +# 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. +# +# Contributors: +# Bill Michalowski, Red Hat Inc. +# Andrew Theurer, Red Hat Inc. +""" +Moongen Traffic Generator Model +""" + +# python imports +import logging +from collections import OrderedDict +import subprocess +import re + +# VSPerf imports +from conf import settings +from core.results.results_constants import ResultsConstants +from tools.pkt_gen.trafficgen.trafficgenhelper import ( + TRAFFIC_DEFAULTS, + merge_spec) +from tools.pkt_gen.trafficgen.trafficgen import ITrafficGenerator + +class Moongen(ITrafficGenerator): + """Moongen Traffic generator wrapper.""" + _traffic_defaults = TRAFFIC_DEFAULTS.copy() + _logger = logging.getLogger(__name__) + + def __init__(self): + """Moongen class constructor.""" + self._logger.info("In moongen __init__ method") + self._params = {} + self._moongen_host_ip_addr = ( + settings.getValue('TRAFFICGEN_MOONGEN_HOST_IP_ADDR')) + self._moongen_base_dir = ( + settings.getValue('TRAFFICGEN_MOONGEN_BASE_DIR')) + self._moongen_user = settings.getValue('TRAFFICGEN_MOONGEN_USER') + self._moongen_ports = settings.getValue('TRAFFICGEN_MOONGEN_PORTS') + + @property + def traffic_defaults(self): + """Default traffic values. + + These can be expected to be constant across traffic generators, + so no setter is provided. Changes to the structure or contents + will likely break traffic generator implementations or tests + respectively. + """ + self._logger.info("In moongen traffic_defaults method") + return self._traffic_defaults + + def create_moongen_cfg_file(self, traffic, duration=60, + acceptable_loss_pct=1, one_shot=0): + """Create the MoonGen configuration file from VSPERF's traffic profile + :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags + :param duration: The length of time to generate packet throughput + :param acceptable_loss: Maximum packet loss acceptable + :param one_shot: No RFC 2544 binary search, + just packet flow at traffic specifics + """ + logging.debug("traffic['frame_rate'] = " + \ + str(traffic['frame_rate'])) + + logging.debug("traffic['multistream'] = " + \ + str(traffic['multistream'])) + + logging.debug("traffic['stream_type'] = " + \ + str(traffic['stream_type'])) + + logging.debug("traffic['l2']['srcmac'] = " + \ + str(traffic['l2']['srcmac'])) + + logging.debug("traffic['l2']['dstmac'] = " + \ + str(traffic['l2']['dstmac'])) + + logging.debug("traffic['l3']['proto'] = " + \ + str(traffic['l3']['proto'])) + + logging.debug("traffic['l3']['srcip'] = " + \ + str(traffic['l3']['srcip'])) + + logging.debug("traffic['l3']['dstip'] = " + \ + str(traffic['l3']['dstip'])) + + logging.debug("traffic['l4']['srcport'] = " + \ + str(traffic['l4']['srcport'])) + + logging.debug("traffic['l4']['dstport'] = " + \ + str(traffic['l4']['dstport'])) + + logging.debug("traffic['vlan']['enabled'] = " + \ + str(traffic['vlan']['enabled'])) + + logging.debug("traffic['vlan']['id'] = " + \ + str(traffic['vlan']['id'])) + + logging.debug("traffic['vlan']['priority'] = " + \ + str(traffic['vlan']['priority'])) + + logging.debug("traffic['vlan']['cfi'] = " + \ + str(traffic['vlan']['cfi'])) + + logging.debug(traffic['l2']['framesize']) + + out_file = open("opnfv-vsperf-cfg.lua", "wt") + + out_file.write("VSPERF {\n") + + out_file.write("testType = \"throughput\",\n") + + out_file.write("runBidirec = " + \ + traffic['bidir'].lower() + ",\n") + + out_file.write("frameSize = " + \ + str(traffic['l2']['framesize']) + ",\n") + + out_file.write("srcMac = \"" + \ + str(traffic['l2']['srcmac']) + "\",\n") + + out_file.write("dstMac = \"" + \ + str(traffic['l2']['dstmac']) + "\",\n") + + out_file.write("srcIp = \"" + \ + str(traffic['l3']['srcip']) + "\",\n") + + out_file.write("dstIp = \"" + \ + str(traffic['l3']['dstip']) + "\",\n") + + out_file.write("vlanId = " + \ + str(traffic['vlan']['id']) + ",\n") + + out_file.write("searchRunTime = " + \ + str(duration) + ",\n") + + out_file.write("validationRunTime = " + \ + str(duration) + ",\n") + + out_file.write("acceptableLossPct = " + \ + str(acceptable_loss_pct) + ",\n") + + out_file.write("ports = " +\ + str(self._moongen_ports) + ",\n") + + if one_shot: + out_file.write("oneShot = true,\n") + + # Assume 10G line rates at the moment. Need to convert VSPERF + # frame_rate (percentage of line rate) to Mpps for MoonGen + + out_file.write("startRate = " + str((traffic['frame_rate'] / 100) * 14.88) + "\n") + out_file.write("}" + "\n") + out_file.close() + + copy_moongen_cfg = "scp opnfv-vsperf-cfg.lua " + \ + self._moongen_user + "@" + \ + self._moongen_host_ip_addr + ":" + \ + self._moongen_base_dir + \ + "/. && rm opnfv-vsperf-cfg.lua" + + find_moongen = subprocess.Popen(copy_moongen_cfg, + shell=True, + stderr=subprocess.PIPE) + + output, error = find_moongen.communicate() + + if error: + logging.error(output) + logging.error(error) + raise RuntimeError('MOONGEN: Error copying configuration file') + + def connect(self): + """Connect to MoonGen traffic generator + + Verify that MoonGen is on the system indicated by + the configuration file + """ + self._logger.info("MOONGEN: In MoonGen connect method...") + + if self._moongen_host_ip_addr: + cmd_ping = "ping -c1 " + self._moongen_host_ip_addr + else: + raise RuntimeError('MOONGEN: MoonGen host not defined') + + ping = subprocess.Popen(cmd_ping, shell=True, stderr=subprocess.PIPE) + output, error = ping.communicate() + + if ping.returncode: + self._logger.error(error) + self._logger.error(output) + raise RuntimeError('MOONGEN: Cannot ping MoonGen host at ' + \ + self._moongen_host_ip_addr) + + connect_moongen = "ssh " + self._moongen_user + \ + "@" + self._moongen_host_ip_addr + + cmd_find_moongen = connect_moongen + " ls " + \ + self._moongen_base_dir + "/examples/opnfv-vsperf.lua" + + find_moongen = subprocess.Popen(cmd_find_moongen, + shell=True, + stderr=subprocess.PIPE) + + output, error = find_moongen.communicate() + + if find_moongen.returncode: + self._logger.error(error) + self._logger.error(output) + raise RuntimeError( + 'MOONGEN: Cannot locate MoonGen program at %s within %s' \ + % (self._moongen_host_ip_addr, self._moongen_base_dir)) + + self._logger.info("MOONGEN: MoonGen host successfully found...") + + def disconnect(self): + """Disconnect from the traffic generator. + + As with :func:`connect`, this function is optional. + + Where implemented, this function should raise an exception on + failure. + + :returns: None + """ + self._logger.info("MOONGEN: In moongen disconnect method") + + def send_burst_traffic(self, traffic=None, numpkts=100, duration=20): + """Send a burst of traffic. + + Send a ``numpkts`` packets of traffic, using ``traffic`` + configuration, with a timeout of ``time``. + + :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags + :param numpkts: Number of packets to send + :param duration: Time to wait to receive packets + + :returns: dictionary of strings with following data: + - List of Tx Frames, + - List of Rx Frames, + - List of Tx Bytes, + - List of List of Rx Bytes, + - Payload Errors and Sequence Errors. + """ + self._logger.info("In moongen send_burst_traffic method") + return NotImplementedError('Moongen Burst traffic not implemented') + + def send_cont_traffic(self, traffic=None, duration=20): + """Send a continuous flow of traffic + + Send packets at ``frame rate``, using ``traffic`` configuration, + until timeout ``time`` occurs. + + :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags + :param duration: Time to wait to receive packets (secs) + :returns: dictionary of strings with following data: + - Tx Throughput (fps), + - Rx Throughput (fps), + - Tx Throughput (mbps), + - Rx Throughput (mbps), + - Tx Throughput (% linerate), + - Rx Throughput (% linerate), + - Min Latency (ns), + - Max Latency (ns), + - Avg Latency (ns) + """ + self._logger.info("In moongen send_cont_traffic method") + + self._params.clear() + self._params['traffic'] = self.traffic_defaults.copy() + + if traffic: + self._params['traffic'] = merge_spec(self._params['traffic'], + traffic) + + Moongen.create_moongen_cfg_file(self, + traffic, + duration=duration, + acceptable_loss_pct=100.0, + one_shot=1) + + collected_results = Moongen.run_moongen_and_collect_results(self, + test_run=1) + + total_throughput_rx_fps = ( + float(collected_results[ResultsConstants.THROUGHPUT_RX_FPS])) + + total_throughput_rx_mbps = ( + float(collected_results[ResultsConstants.THROUGHPUT_RX_MBPS])) + + total_throughput_rx_pct = ( + float(collected_results[ResultsConstants.THROUGHPUT_RX_PERCENT])) + + total_throughput_tx_fps = ( + float(collected_results[ResultsConstants.TX_RATE_FPS])) + + total_throughput_tx_mbps = ( + float(collected_results[ResultsConstants.TX_RATE_MBPS])) + + total_throughput_tx_pct = ( + float(collected_results[ResultsConstants.TX_RATE_PERCENT])) + + total_min_latency_ns = 0 + total_max_latency_ns = 0 + total_avg_latency_ns = 0 + + results = OrderedDict() + results[ResultsConstants.THROUGHPUT_RX_FPS] = ( + '{:,.6f}'.format(total_throughput_rx_fps)) + + results[ResultsConstants.THROUGHPUT_RX_MBPS] = ( + '{:,.3f}'.format(total_throughput_rx_mbps)) + + results[ResultsConstants.THROUGHPUT_RX_PERCENT] = ( + '{:,.3f}'.format(total_throughput_rx_pct)) + + results[ResultsConstants.TX_RATE_FPS] = ( + '{:,.6f}'.format(total_throughput_tx_fps)) + + results[ResultsConstants.TX_RATE_MBPS] = ( + '{:,.3f}'.format(total_throughput_tx_mbps)) + + results[ResultsConstants.TX_RATE_PERCENT] = ( + '{:,.3f}'.format(total_throughput_tx_pct)) + + results[ResultsConstants.MIN_LATENCY_NS] = ( + '{:,.3f}'.format(total_min_latency_ns)) + + results[ResultsConstants.MAX_LATENCY_NS] = ( + '{:,.3f}'.format(total_max_latency_ns)) + + results[ResultsConstants.AVG_LATENCY_NS] = ( + '{:,.3f}'.format(total_avg_latency_ns)) + + return results + + def start_cont_traffic(self, traffic=None, duration=20): + """ Non-blocking version of 'send_cont_traffic'. + + Start transmission and immediately return. Do not wait for + results. + :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags + :param duration: Time to wait to receive packets (secs) + """ + self._logger.info("In moongen start_cont_traffic method") + return NotImplementedError('Moongen continuous traffic not implemented') + + def stop_cont_traffic(self): + # Stop continuous transmission and return results. + self._logger.info("In moongen stop_cont_traffic method") + + def run_moongen_and_collect_results(self, test_run=1): + """Execute MoonGen and transform results into VSPERF format + :param test_run: The number of tests to run + """ + # Start MoonGen and create logfile of the run + connect_moongen = "ssh " + self._moongen_user + "@" + \ + self._moongen_host_ip_addr + + cmd_moongen = " 'cd " + self._moongen_base_dir + \ + "; ./build/MoonGen examples/opnfv-vsperf.lua | tee moongen_log.txt'" + + cmd_start_moongen = connect_moongen + cmd_moongen + + start_moongen = subprocess.Popen(cmd_start_moongen, + shell=True, stderr=subprocess.PIPE) + + output, error = start_moongen.communicate() + + if start_moongen.returncode: + logging.debug(error) + logging.debug(output) + raise RuntimeError( + 'MOONGEN: Error starting MoonGen program at %s within %s' \ + % (self._moongen_host_ip_addr, self._moongen_base_dir)) + + cmd_moongen = "mkdir -p /tmp/moongen/" + str(test_run) + + moongen_create_log_dir = subprocess.Popen(cmd_moongen, + shell=True, + stderr=subprocess.PIPE) + + output, error = moongen_create_log_dir.communicate() + + if moongen_create_log_dir.returncode: + logging.debug(error) + logging.debug(output) + raise RuntimeError( + 'MOONGEN: Error obtaining MoonGen log from %s within %s' \ + % (self._moongen_host_ip_addr, self._moongen_base_dir)) + + cmd_moongen = " scp " + self._moongen_user + "@" + \ + self._moongen_host_ip_addr + ":" + \ + self._moongen_base_dir + "/moongen_log.txt /tmp/moongen/" + \ + str(test_run) + "/moongen-run.log" + + copy_moongen_log = subprocess.Popen(cmd_moongen, + shell=True, + stderr=subprocess.PIPE) + + output, error = copy_moongen_log.communicate() + + if copy_moongen_log.returncode: + logging.debug(error) + logging.debug(output) + raise RuntimeError( + 'MOONGEN: Error obtaining MoonGen log from %s within %s' \ + % (self._moongen_host_ip_addr, self._moongen_base_dir)) + + log_file = "/tmp/moongen/" + str(test_run) + "/moongen-run.log" + + with open(log_file, 'r') as logfile_handle: + mytext = logfile_handle.read() + + # REPORT results line + # match.group(1) = Tx frames + # match.group(2) = Rx frames + # match.group(3) = Frame loss (count) + # match.group(4) = Frame loss (percentage) + # match.group(5) = Tx Mpps + # match.group(6) = Rx Mpps + search_pattern = re.compile( + r'\[REPORT\]\s+total\:\s+' + r'Tx\s+frames\:\s+(\d+)\s+' + r'Rx\s+Frames\:\s+(\d+)\s+' + r'frame\s+loss\:\s+(\d+)\,' + r'\s+(\d+\.\d+|\d+)%\s+' + r'Tx\s+Mpps\:\s+(\d+.\d+|\d+)\s+' + r'Rx\s+Mpps\:\s+(\d+\.\d+|\d+)', + re.IGNORECASE) + + results_match = search_pattern.search(mytext) + + if not results_match: + logging.error('There was a problem parsing ' +\ + 'MoonGen REPORT section of MoonGen log file') + + moongen_results = OrderedDict() + moongen_results[ResultsConstants.THROUGHPUT_RX_FPS] = 0 + moongen_results[ResultsConstants.THROUGHPUT_RX_MBPS] = 0 + moongen_results[ResultsConstants.THROUGHPUT_RX_PERCENT] = 0 + moongen_results[ResultsConstants.TX_RATE_FPS] = 0 + moongen_results[ResultsConstants.TX_RATE_MBPS] = 0 + moongen_results[ResultsConstants.TX_RATE_PERCENT] = 0 + moongen_results[ResultsConstants.B2B_TX_COUNT] = 0 + moongen_results[ResultsConstants.B2B_FRAMES] = 0 + moongen_results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = 0 + moongen_results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = 0 + + # find PARAMETERS line + # parameters_match.group(1) = Frame size + + search_pattern = re.compile( + r'\[PARAMETERS\]\s+.*frameSize\:\s+(\d+)', + flags=re.IGNORECASE) + parameters_match = search_pattern.search(mytext) + + if parameters_match: + frame_size = int(parameters_match.group(1)) + else: + logging.error('There was a problem parsing MoonGen ' +\ + 'PARAMETERS section of MoonGen log file') + frame_size = 0 + + if results_match and parameters_match: + # Assume for now 10G link speed + max_theoretical_mfps = ( + (10000000000 / 8) / (frame_size + 20)) + + moongen_results[ResultsConstants.THROUGHPUT_RX_FPS] = ( + float(results_match.group(6)) * 1000000) + + moongen_results[ResultsConstants.THROUGHPUT_RX_MBPS] = ( + (float(results_match.group(6)) * frame_size + 20) * 8) + + moongen_results[ResultsConstants.THROUGHPUT_RX_PERCENT] = ( + float(results_match.group(6)) * \ + 1000000 / max_theoretical_mfps * 100) + + moongen_results[ResultsConstants.TX_RATE_FPS] = ( + float(results_match.group(5)) * 1000000) + + moongen_results[ResultsConstants.TX_RATE_MBPS] = ( + float(results_match.group(5)) * (frame_size + 20) * 8) + + moongen_results[ResultsConstants.TX_RATE_PERCENT] = ( + float(results_match.group(5)) * + 1000000 / max_theoretical_mfps * 100) + + moongen_results[ResultsConstants.B2B_TX_COUNT] = ( + float(results_match.group(1))) + + moongen_results[ResultsConstants.B2B_FRAMES] = ( + float(results_match.group(2))) + + moongen_results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = ( + float(results_match.group(3))) + + moongen_results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = ( + float(results_match.group(4))) + + return moongen_results + + def send_rfc2544_throughput(self, traffic=None, duration=20, + lossrate=0.0, trials=1): + # + # Send traffic per RFC2544 throughput test specifications. + # + # Send packets at a variable rate, using ``traffic`` + # configuration, until minimum rate at which no packet loss is + # detected is found. + # + # :param traffic: Detailed "traffic" spec, see design docs for details + # :param trials: Number of trials to execute + # :param duration: Per iteration duration + # :param lossrate: Acceptable lossrate percentage + # :returns: dictionary of strings with following data: + # - Tx Throughput (fps), + # - Rx Throughput (fps), + # - Tx Throughput (mbps), + # - Rx Throughput (mbps), + # - Tx Throughput (% linerate), + # - Rx Throughput (% linerate), + # - Min Latency (ns), + # - Max Latency (ns), + # - Avg Latency (ns) + # + self._logger.info("In moongen send_rfc2544_throughput method") + self._params.clear() + self._params['traffic'] = self.traffic_defaults.copy() + + if traffic: + self._params['traffic'] = merge_spec(self._params['traffic'], + traffic) + Moongen.create_moongen_cfg_file(self, + traffic, + duration=duration, + acceptable_loss_pct=lossrate) + + total_throughput_rx_fps = 0 + total_throughput_rx_mbps = 0 + total_throughput_rx_pct = 0 + total_throughput_tx_fps = 0 + total_throughput_tx_mbps = 0 + total_throughput_tx_pct = 0 + total_min_latency_ns = 0 + total_max_latency_ns = 0 + total_avg_latency_ns = 0 + + for test_run in range(1, trials+1): + collected_results = ( + Moongen.run_moongen_and_collect_results(self, test_run=test_run)) + + total_throughput_rx_fps += ( + float(collected_results[ResultsConstants.THROUGHPUT_RX_FPS])) + + total_throughput_rx_mbps += ( + float(collected_results[ResultsConstants.THROUGHPUT_RX_MBPS])) + + total_throughput_rx_pct += ( + float(collected_results[ResultsConstants.THROUGHPUT_RX_PERCENT])) + + total_throughput_tx_fps += ( + float(collected_results[ResultsConstants.TX_RATE_FPS])) + + total_throughput_tx_mbps += ( + float(collected_results[ResultsConstants.TX_RATE_MBPS])) + + total_throughput_tx_pct += ( + float(collected_results[ResultsConstants.TX_RATE_PERCENT])) + + # Latency not supported now, leaving as placeholder + total_min_latency_ns = 0 + total_max_latency_ns = 0 + total_avg_latency_ns = 0 + + results = OrderedDict() + results[ResultsConstants.THROUGHPUT_RX_FPS] = ( + '{:,.6f}'.format(total_throughput_rx_fps / trials)) + + results[ResultsConstants.THROUGHPUT_RX_MBPS] = ( + '{:,.3f}'.format(total_throughput_rx_mbps / trials)) + + results[ResultsConstants.THROUGHPUT_RX_PERCENT] = ( + '{:,.3f}'.format(total_throughput_rx_pct / trials)) + + results[ResultsConstants.TX_RATE_FPS] = ( + '{:,.6f}'.format(total_throughput_tx_fps / trials)) + + results[ResultsConstants.TX_RATE_MBPS] = ( + '{:,.3f}'.format(total_throughput_tx_mbps / trials)) + + results[ResultsConstants.TX_RATE_PERCENT] = ( + '{:,.3f}'.format(total_throughput_tx_pct / trials)) + + results[ResultsConstants.MIN_LATENCY_NS] = ( + '{:,.3f}'.format(total_min_latency_ns / trials)) + + results[ResultsConstants.MAX_LATENCY_NS] = ( + '{:,.3f}'.format(total_max_latency_ns / trials)) + + results[ResultsConstants.AVG_LATENCY_NS] = ( + '{:,.3f}'.format(total_avg_latency_ns / trials)) + + return results + + def start_rfc2544_throughput(self, traffic=None, trials=3, duration=20, + lossrate=0.0): + """Non-blocking version of 'send_rfc2544_throughput'. + + Start transmission and immediately return. Do not wait for + results. + """ + self._logger.info( + "MOONGEN: In moongen start_rfc2544_throughput method") + + def wait_rfc2544_throughput(self): + """Wait for and return results of RFC2544 test. + """ + self._logger.info('In moongen wait_rfc2544_throughput') + + def send_rfc2544_back2back(self, traffic=None, duration=60, + lossrate=0.0, trials=1): + """Send traffic per RFC2544 back2back test specifications. + + Send packets at a fixed rate, using ``traffic`` + configuration, for duration seconds. + + :param traffic: Detailed "traffic" spec, see design docs for details + :param trials: Number of trials to execute + :param duration: Per iteration duration + :param lossrate: Acceptable loss percentage + + :returns: Named tuple of Rx Throughput (fps), Rx Throughput (mbps), + Tx Rate (% linerate), Rx Rate (% linerate), Tx Count (frames), + Back to Back Count (frames), Frame Loss (frames), Frame Loss (%) + :rtype: :class:`Back2BackResult` + """ + self._params.clear() + self._params['traffic'] = self.traffic_defaults.copy() + + if traffic: + self._params['traffic'] = merge_spec(self._params['traffic'], + traffic) + + Moongen.create_moongen_cfg_file(self, + traffic, + duration=duration, + acceptable_loss_pct=lossrate) + + results = OrderedDict() + results[ResultsConstants.B2B_RX_FPS] = 0 + results[ResultsConstants.B2B_TX_FPS] = 0 + results[ResultsConstants.B2B_RX_PERCENT] = 0 + results[ResultsConstants.B2B_TX_PERCENT] = 0 + results[ResultsConstants.B2B_TX_COUNT] = 0 + results[ResultsConstants.B2B_FRAMES] = 0 + results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = 0 + results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = 0 + results[ResultsConstants.SCAL_STREAM_COUNT] = 0 + results[ResultsConstants.SCAL_STREAM_TYPE] = 0 + results[ResultsConstants.SCAL_PRE_INSTALLED_FLOWS] = 0 + + for test_run in range(1, trials+1): + collected_results = ( + Moongen.run_moongen_and_collect_results(self, test_run=test_run)) + + results[ResultsConstants.B2B_RX_FPS] += ( + float(collected_results[ResultsConstants.THROUGHPUT_RX_FPS])) + + results[ResultsConstants.B2B_RX_PERCENT] += ( + float(collected_results[ResultsConstants.THROUGHPUT_RX_PERCENT])) + + results[ResultsConstants.B2B_TX_FPS] += ( + float(collected_results[ResultsConstants.TX_RATE_FPS])) + + results[ResultsConstants.B2B_TX_PERCENT] += ( + float(collected_results[ResultsConstants.TX_RATE_PERCENT])) + + results[ResultsConstants.B2B_TX_COUNT] += ( + int(collected_results[ResultsConstants.B2B_TX_COUNT])) + + results[ResultsConstants.B2B_FRAMES] += ( + int(collected_results[ResultsConstants.B2B_FRAMES])) + + results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] += ( + int(collected_results[ResultsConstants.B2B_FRAME_LOSS_FRAMES])) + + results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] += ( + int(collected_results[ResultsConstants.B2B_FRAME_LOSS_PERCENT])) + + # Calculate average results + results[ResultsConstants.B2B_RX_FPS] = ( + results[ResultsConstants.B2B_RX_FPS] / trials) + + results[ResultsConstants.B2B_RX_PERCENT] = ( + results[ResultsConstants.B2B_RX_PERCENT] / trials) + + results[ResultsConstants.B2B_TX_FPS] = ( + results[ResultsConstants.B2B_TX_FPS] / trials) + + results[ResultsConstants.B2B_TX_PERCENT] = ( + results[ResultsConstants.B2B_TX_PERCENT] / trials) + + results[ResultsConstants.B2B_TX_COUNT] = ( + results[ResultsConstants.B2B_TX_COUNT] / trials) + + results[ResultsConstants.B2B_FRAMES] = ( + results[ResultsConstants.B2B_FRAMES] / trials) + + results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] = ( + results[ResultsConstants.B2B_FRAME_LOSS_FRAMES] / trials) + + results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = ( + results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] / trials) + + results[ResultsConstants.SCAL_STREAM_COUNT] = 0 + results[ResultsConstants.SCAL_STREAM_TYPE] = 0 + results[ResultsConstants.SCAL_PRE_INSTALLED_FLOWS] = 0 + + return results + + def start_rfc2544_back2back(self, traffic=None, trials=1, duration=20, + lossrate=0.0): + # + # Non-blocking version of 'send_rfc2544_back2back'. + # + # Start transmission and immediately return. Do not wait for results. + # + self._logger.info("In moongen start_rfc2544_back2back method") + return NotImplementedError( + 'Moongen start back2back traffic not implemented') + + def wait_rfc2544_back2back(self): + self._logger.info("In moongen wait_rfc2544_back2back method") + # + # Wait and set results of RFC2544 test. + # + return NotImplementedError( + 'Moongen wait back2back traffic not implemented') + +if __name__ == "__main__": + pass diff --git a/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py b/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py index 428240a1..91f7e27f 100644 --- a/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py +++ b/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py @@ -169,6 +169,11 @@ def main(): default="PAIR", help="The traffic pattern between endpoints", dest="traffic_pattern") + optional_named.add_argument("--traffic_custom", + required=False, + default=None, + help="The traffic pattern between endpoints", + dest="traffic_custom") optional_named.add_argument("--search_mode", required=False, choices=["COMBO", "STEP", "BINARY"], @@ -318,6 +323,12 @@ def main(): logger.debug("Creating project ...") project = stc.get("System1", "children-Project") + # Configure any custom traffic parameters + if args.traffic_custom == "cont": + if args.verbose: + logger.debug("Configure Continuous Traffic") + stc.create("ContinuousTestConfig", under=project) + # Create ports if args.verbose: logger.debug("Creating ports ...") diff --git a/tools/pkt_gen/testcenter/testcenter.py b/tools/pkt_gen/testcenter/testcenter.py index 2adf6c90..f05ce337 100644 --- a/tools/pkt_gen/testcenter/testcenter.py +++ b/tools/pkt_gen/testcenter/testcenter.py @@ -55,9 +55,125 @@ class TestCenter(trafficgen.ITrafficGenerator): def send_cont_traffic(self, traffic=None, duration=30): """ - Do nothing. + Send Custom - Continuous Test traffic + Reuse RFC2544 throughput test specifications along with + 'custom' configuration """ - return None + verbose = False + custom = "cont" + framesize = settings.getValue("TRAFFICGEN_STC_FRAME_SIZE") + if traffic and 'l2' in traffic: + if 'framesize' in traffic['l2']: + framesize = traffic['l2']['framesize'] + 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"), + "--traffic_custom", + str(custom), + "--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", + str(framesize), + "--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 + self._logger.debug("Arguments used to call test: %s", args) + subprocess.check_call(args) + + filec = os.path.join(settings.getValue("TRAFFICGEN_STC_RESULTS_DIR"), + settings.getValue( + "TRAFFICGEN_STC_CSV_RESULTS_FILE_PREFIX") + + ".csv") + + if verbose: + self._logger.info("file: %s", filec) + + result = {} + + with open(filec, "r") as csvfile: + csvreader = csv.DictReader(csvfile) + for row in csvreader: + self._logger.info("Row: %s", row) + tx_fps = ((float(row["TxFrameCount"])) / + (float(row["Duration(sec)"]))) + rx_fps = ((float(row["RxFrameCount"])) / + (float(row["Duration(sec)"]))) + tx_mbps = ((float(row["TxFrameCount"]) * + float(row["ConfiguredFrameSize"])) / + (float(row["Duration(sec)"]) * 1000000.0)) + rx_mbps = ((float(row["RxFrameCount"]) * + float(row["ConfiguredFrameSize"])) / + (float(row["Duration(sec)"]) * 1000000.0)) + result[ResultsConstants.TX_RATE_FPS] = tx_fps + result[ResultsConstants.THROUGHPUT_RX_FPS] = rx_fps + result[ResultsConstants.TX_RATE_MBPS] = tx_mbps + result[ResultsConstants.THROUGHPUT_RX_MBPS] = rx_mbps + result[ResultsConstants.TX_RATE_PERCENT] = float( + row["OfferedLoad(%)"]) + result[ResultsConstants.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 + result[ResultsConstants.FRAME_LOSS_PERCENT] = float( + row["PercentLoss"]) + return result def send_rfc2544_throughput(self, traffic=None, trials=3, duration=20, lossrate=0.0): diff --git a/tools/pkt_gen/xena/profiles/baseconfig.x2544 b/tools/pkt_gen/xena/profiles/baseconfig.x2544 index 3ebe79dc..0612b329 100644 --- a/tools/pkt_gen/xena/profiles/baseconfig.x2544 +++ b/tools/pkt_gen/xena/profiles/baseconfig.x2544 @@ -207,7 +207,7 @@ "PassThreshold": 0.0 }, "ReportPropertyOptions": [ - "b" + "LatencyCounters" ], "TestType": "Throughput", "Enabled": false, diff --git a/tools/pkt_gen/xena/xena.py b/tools/pkt_gen/xena/xena.py index 194de343..7dd4b90b 100755 --- a/tools/pkt_gen/xena/xena.py +++ b/tools/pkt_gen/xena/xena.py @@ -30,6 +30,8 @@ import sys from time import sleep import xml.etree.ElementTree as ET from collections import OrderedDict +# scapy imports +import scapy.layers.inet as inet # VSPerf imports from conf import settings @@ -48,10 +50,6 @@ from tools.pkt_gen.xena.XenaDriver import ( XenaManager, ) -# scapy imports -import scapy.layers.inet as inet - - class Xena(ITrafficGenerator): """ Xena Traffic generator wrapper class @@ -94,11 +92,11 @@ class Xena(ITrafficGenerator): if test_type == 'Throughput': results = OrderedDict() - results[ResultsConstants.THROUGHPUT_RX_FPS] = int( - root[0][1][0][0].get('PortRxPps')) + int( + results[ResultsConstants.THROUGHPUT_RX_FPS] = float( + root[0][1][0][0].get('PortRxPps')) + float( root[0][1][0][1].get('PortRxPps')) - results[ResultsConstants.THROUGHPUT_RX_MBPS] = (int( - root[0][1][0][0].get('PortRxBpsL1')) + int( + results[ResultsConstants.THROUGHPUT_RX_MBPS] = (float( + root[0][1][0][0].get('PortRxBpsL1')) + float( root[0][1][0][1].get('PortRxBpsL1')))/ 1000000 results[ResultsConstants.THROUGHPUT_RX_PERCENT] = ( 100 - int(root[0][1][0].get('TotalLossRatioPcnt'))) * float( @@ -288,14 +286,23 @@ class Xena(ITrafficGenerator): 1, settings.getValue("TRAFFICGEN_XENA_PORT1_IP"), settings.getValue("TRAFFICGEN_XENA_PORT1_CIDR"), settings.getValue("TRAFFICGEN_XENA_PORT1_GATEWAY")) - j_file.set_test_options( - packet_sizes=self._params['traffic']['l2']['framesize'], - iterations=trials, loss_rate=loss_rate, - duration=self._duration, micro_tpld=True if self._params[ - 'traffic']['l2']['framesize'] == 64 else False) + if testtype == '2544_throughput': + j_file.set_test_options_tput( + packet_sizes=self._params['traffic']['l2']['framesize'], + iterations=trials, loss_rate=loss_rate, + duration=self._duration, micro_tpld=True if self._params[ + 'traffic']['l2']['framesize'] == 64 else False) j_file.enable_throughput_test() + elif testtype == '2544_b2b': + j_file.set_test_options_back2back( + packet_sizes=self._params['traffic']['l2']['framesize'], + iterations=trials, duration=self._duration, + startvalue=self._params['traffic']['frame_rate'], + endvalue=self._params['traffic']['frame_rate'], + micro_tpld=True if self._params[ + 'traffic']['l2']['framesize'] == 64 else False) j_file.enable_back2back_test() j_file.set_header_layer2( @@ -485,22 +492,18 @@ class Xena(ITrafficGenerator): def send_burst_traffic(self, traffic=None, numpkts=100, duration=20): """Send a burst of traffic. - Send a ``numpkts`` packets of traffic, using ``traffic`` - configuration, with a timeout of ``time``. + See ITrafficGenerator for description + """ + self._duration = duration - Attributes: - :param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags - :param numpkts: Number of packets to send - :param duration: Time to wait to receive packets + self._params.clear() + self._params['traffic'] = self.traffic_defaults.copy() + if traffic: + self._params['traffic'] = merge_spec(self._params['traffic'], + traffic) - :returns: dictionary of strings with following data: - - List of Tx Frames, - - List of Rx Frames, - - List of Tx Bytes, - - List of List of Rx Bytes, - - Payload Errors and Sequence Errors. - """ - raise NotImplementedError('Xena burst traffic not implemented') + self._start_traffic_api(numpkts) + return self._stop_api_traffic() def send_cont_traffic(self, traffic=None, duration=20): """Send a continuous flow of traffic. diff --git a/tools/pkt_gen/xena/xena_json.py b/tools/pkt_gen/xena/xena_json.py index 23b56084..2a15a932 100644 --- a/tools/pkt_gen/xena/xena_json.py +++ b/tools/pkt_gen/xena/xena_json.py @@ -354,10 +354,10 @@ class XenaJSON(object): self.json_data['PortHandler']['EntityList'][ port]["IpV6RoutingPrefix"] = int(netmask) - def set_test_options(self, packet_sizes, duration, iterations, loss_rate, - micro_tpld=False): + def set_test_options_tput(self, packet_sizes, duration, iterations, + loss_rate, micro_tpld=False): """ - Set the test options + Set the tput test options :param packet_sizes: List of packet sizes to test, single int entry is acceptable for one packet size testing :param duration: time for each test in seconds as int @@ -379,6 +379,35 @@ class XenaJSON(object): self.json_data['TestOptions']['TestTypeOptionMap']['Throughput'][ 'Iterations'] = iterations + def set_test_options_back2back(self, packet_sizes, duration, + iterations, startvalue, endvalue, + micro_tpld=False): + """ + Set the back2back test options + :param packet_sizes: List of packet sizes to test, single int entry is + acceptable for one packet size testing + :param duration: time for each test in seconds as int + :param iterations: number of iterations of testing as int + :param micro_tpld: boolean if micro_tpld should be enabled or disabled + :param StartValue: start value + :param EndValue: end value + :return: None + """ + if isinstance(packet_sizes, int): + packet_sizes = [packet_sizes] + self.json_data['TestOptions']['PacketSizes'][ + 'CustomPacketSizes'] = packet_sizes + self.json_data['TestOptions']['TestTypeOptionMap']['Back2Back'][ + 'Duration'] = duration + self.json_data['TestOptions']['FlowCreationOptions'][ + 'UseMicroTpldOnDemand'] = 'true' if micro_tpld else 'false' + self.json_data['TestOptions']['TestTypeOptionMap']['Back2Back'][ + 'Iterations'] = iterations + self.json_data['TestOptions']['TestTypeOptionMap']['Back2Back'][ + 'RateSweepOptions']['StartValue'] = startvalue + self.json_data['TestOptions']['TestTypeOptionMap']['Back2Back'][ + 'RateSweepOptions']['EndValue'] = endvalue + def set_topology_blocks(self): """ Set the test topology to a West to East config for half duplex flow with @@ -586,8 +615,8 @@ if __name__ == "__main__": JSON.set_header_layer3(src_ip='192.168.100.2', dst_ip='192.168.100.3', protocol='udp') JSON.set_header_layer4_udp(source_port=3000, destination_port=3001) - JSON.set_test_options(packet_sizes=[64], duration=10, iterations=1, - loss_rate=0.0, micro_tpld=True) + JSON.set_test_options_tput(packet_sizes=[64], duration=10, iterations=1, + loss_rate=0.0, micro_tpld=True) JSON.add_header_segments(flows=4000, multistream_layer='L4') JSON.set_topology_blocks() write_json_file(JSON.json_data, './testthis.x2544') |