From 94845d2bf7416d8b59e2eaf017244832cf3277f4 Mon Sep 17 00:00:00 2001 From: fmenguy Date: Tue, 22 Sep 2020 17:10:10 +0200 Subject: NFVBENCH-177: Add a config item 'user_info' and theoretical max rate value Change-Id: If96ccbffab67cfc0a08279d94cf7a5e81d958044 Signed-off-by: fmenguy --- nfvbench/cfg.default.yaml | 41 +++++++++++++++++++++-- nfvbench/nfvbench.py | 64 +++++++++++++++++++++++++++++++++++- nfvbench/summarizer.py | 6 ++++ nfvbench/traffic_client.py | 27 +++++++++++---- nfvbench/traffic_gen/dummy.py | 2 ++ nfvbench/traffic_gen/traffic_base.py | 31 +++++++++++++++++ nfvbench/traffic_gen/trex_gen.py | 3 ++ 7 files changed, 164 insertions(+), 10 deletions(-) (limited to 'nfvbench') diff --git a/nfvbench/cfg.default.yaml b/nfvbench/cfg.default.yaml index 253e8bc..b33d02c 100755 --- a/nfvbench/cfg.default.yaml +++ b/nfvbench/cfg.default.yaml @@ -331,6 +331,26 @@ restart: false # if empty defaults to the one specified in generator_profile.cores cores: +# Simpler override for the interface speed +# if empty, the current generator_profile.intf_speed parameter applies +# if value = 'auto' the auto-detection is forced +intf_speed: + +# 'cores' and 'intf_speed' parameters can be overriden themselves +# by respective options --cores and --intf-speed on the command-line. + +# By default, the real ports line rate is detected and used as +# the reference for computing the theoretical maximum traffic load (100%). +# Note that specifying 'intf_speed' allows to artificially lower this +# reference while not modifying the actual transmission bit rate. + +# The values of the following parameters are ignored on entry +# they are defined here in order to appear in the reported configuration. +# They will reflect the value active at run-time (after overriding or detection) +cores_used: +intf_speed_used: +intf_speed_detected: + # Add cache size in packet generation for TRex field engine (FE). # More information for TRex performance: # https://trex-tgn.cisco.com/trex/doc/trex_stateless.html#_tutorial_field_engine_significantly_improve_performance @@ -338,6 +358,8 @@ cores: # If cache_size < 0: cache_size will be set to flow count value cache_size: 0 # The cache size is actually limited by the number of 64B mbufs configured in the trex platform configuration (see Trex manual 6.2.2. Memory section configuration) +# Note that the resulting value is finally clipped to 10000, whatever the requested size is (by design limitation). + # Trex will use 1 x 64B mbuf per pre-built cached packet, assuming 1 pre-built cached packet per flow, it means for very large number of flows, the number of configured mbuf_64 will need to be set accordingly. mbuf_64: @@ -808,15 +830,28 @@ factory_class: 'BasicFactory' # Can be overriden by --user-label user_label: - -# THESE FIELDS SHOULD BE USED VERY RARELY +# Custom information to be passed to results post-processing, +# they will be included as is in the json report 'config' branch. +# Useful for documenting or automating further treatments. +# The value is any yaml object (=> open usage) - example: +# |user_info: +# | status: explore +# | description: +# | generator: VM +# | attachment: direct +# | target: lab-pf +# | switch: qfx3500 +# Keys may be merged/overriden using the --user-info command line option +# (the command-line parameter value is expressed as a json object string) +user_info: + +# THESE FIELDS SHOULD BE USED VERY RARELY OR ON PURPOSE # Skip vswitch configuration and retrieving of stats # Can be overriden by --no-vswitch-access # Should be left to the default value (false) no_vswitch_access: false - # Enable service mode for trafic capture from TRex console (for debugging purpose) # Can be overriden by --service-mode # Should be left to the default value (false) diff --git a/nfvbench/nfvbench.py b/nfvbench/nfvbench.py index 651d06b..9b8b3d1 100644 --- a/nfvbench/nfvbench.py +++ b/nfvbench/nfvbench.py @@ -274,6 +274,23 @@ class NFVBench(object): self.config_plugin.validate_config(config, self.specs.openstack) +def bool_arg(x): + """Argument type to be used in parser.add_argument() + When a boolean like value is expected to be given + """ + return (str(x).lower() != 'false') \ + and (str(x).lower() != 'no') \ + and (str(x).lower() != '0') + + +def int_arg(x): + """Argument type to be used in parser.add_argument() + When an integer type value is expected to be given + (returns 0 if argument is invalid, hexa accepted) + """ + return int(x, 0) + + def _parse_opts_from_cli(): parser = argparse.ArgumentParser() @@ -476,6 +493,39 @@ def _parse_opts_from_cli(): metavar='', help='Port to port or port to switch to port L2 loopback with VLAN id') + """Option to allow for passing custom information to results post-processing""" + parser.add_argument('--user-info', dest='user_info', + action='store', + metavar='', + help='Custom data to be included as is in the json report config branch - ' + + ' example, pay attention! no space: ' + + '--user-info=\'{"status":"explore","description":{"target":"lab"' + + ',"ok":true,"version":2020}\'') + + """Option to allow for overriding the NFVbench 'vlan_tagging' option""" + parser.add_argument('--vlan-tagging', dest='vlan_tagging', + type=bool_arg, + metavar='', + action='store', + default=None, + help='Override the NFVbench \'vlan_tagging\' parameter') + + """Option to allow for overriding the T-Rex 'intf_speed' parameter""" + parser.add_argument('--intf-speed', dest='intf_speed', + metavar='', + action='store', + default=None, + help='Override the NFVbench \'intf_speed\' parameter ' + + '(e.g. 10Gbps, auto, 16.72Gbps)') + + """Option to allow for overriding the T-Rex 'cores' parameter""" + parser.add_argument('--cores', dest='cores', + type=int_arg, + metavar='', + action='store', + default=None, + help='Override the T-Rex \'cores\' parameter') + parser.add_argument('--cache-size', dest='cache_size', action='store', default='0', @@ -600,7 +650,8 @@ def main(): config.name = '' if opts.config: # do not check extra_specs in flavor as it can contain any key/value pairs - whitelist_keys = ['extra_specs'] + # the same principle applies also to the optional user_info open property + whitelist_keys = ['extra_specs', 'user_info'] # override default config options with start config at path parsed from CLI # check if it is an inline yaml/json config or a file name if os.path.isfile(opts.config): @@ -619,6 +670,17 @@ def main(): LOG.addHandler(fluent_logger) break + # convert 'user_info' opt from json string to dictionnary + # and merge the result with the current config dictionnary + if opts.user_info: + opts.user_info = json.loads(opts.user_info) + if config.user_info: + config.user_info = config.user_info + opts.user_info + else: + config.user_info = opts.user_info + # hide the option to further _update_config() + opts.user_info = None + # traffic profile override options override_custom_traffic(config, opts.frame_sizes, opts.unidir) diff --git a/nfvbench/summarizer.py b/nfvbench/summarizer.py index 326de10..bbd5908 100644 --- a/nfvbench/summarizer.py +++ b/nfvbench/summarizer.py @@ -421,6 +421,8 @@ class NFVBenchSummarizer(Summarizer): 'rate_bps': analysis['ndr']['rate_bps'], 'rate_pps': analysis['ndr']['rate_pps'], 'offered_tx_rate_bps': analysis['ndr']['stats']['offered_tx_rate_bps'], + 'theoretical_tx_rate_pps': analysis['ndr']['stats']['theoretical_tx_rate_pps'], + 'theoretical_tx_rate_bps': analysis['ndr']['stats']['theoretical_tx_rate_bps'], 'drop_percentage': analysis['ndr']['stats']['overall']['drop_percentage'], 'avg_delay_usec': analysis['ndr']['stats']['overall']['avg_delay_usec'], 'min_delay_usec': analysis['ndr']['stats']['overall']['min_delay_usec'], @@ -455,6 +457,8 @@ class NFVBenchSummarizer(Summarizer): 'rate_bps': analysis['pdr']['rate_bps'], 'rate_pps': analysis['pdr']['rate_pps'], 'offered_tx_rate_bps': analysis['pdr']['stats']['offered_tx_rate_bps'], + 'theoretical_tx_rate_pps': analysis['pdr']['stats']['theoretical_tx_rate_pps'], + 'theoretical_tx_rate_bps': analysis['pdr']['stats']['theoretical_tx_rate_bps'], 'drop_percentage': analysis['pdr']['stats']['overall']['drop_percentage'], 'avg_delay_usec': analysis['pdr']['stats']['overall']['avg_delay_usec'], 'min_delay_usec': analysis['pdr']['stats']['overall']['min_delay_usec'], @@ -480,6 +484,8 @@ class NFVBenchSummarizer(Summarizer): single_run_data = { 'type': 'single_run', 'offered_tx_rate_bps': analysis['stats']['offered_tx_rate_bps'], + 'theoretical_tx_rate_pps': analysis['stats']['theoretical_tx_rate_pps'], + 'theoretical_tx_rate_bps': analysis['stats']['theoretical_tx_rate_bps'], 'drop_rate_percent': analysis['stats']['overall']['drop_rate_percent'], 'avg_delay_usec': analysis['stats']['overall']['rx']['avg_delay_usec'], 'min_delay_usec': analysis['stats']['overall']['rx']['min_delay_usec'], diff --git a/nfvbench/traffic_client.py b/nfvbench/traffic_client.py index e2ae977..72fbb5d 100755 --- a/nfvbench/traffic_client.py +++ b/nfvbench/traffic_client.py @@ -486,15 +486,24 @@ class GeneratorConfig(object): self.cores = config.cores else: self.cores = gen_config.get('cores', 1) + # let's report the value actually used in the end + config.cores_used = self.cores self.mbuf_factor = config.mbuf_factor self.mbuf_64 = config.mbuf_64 self.hdrh = not config.disable_hdrh - if gen_config.intf_speed: - # interface speed is overriden from config - self.intf_speed = bitmath.parse_string(gen_config.intf_speed.replace('ps', '')).bits + if config.intf_speed: + # interface speed is overriden from the command line + self.intf_speed = config.intf_speed + elif gen_config.intf_speed: + # interface speed is overriden from the generator config + self.intf_speed = gen_config.intf_speed else: + self.intf_speed = "auto" + if self.intf_speed == "auto" or self.intf_speed == "0": # interface speed is discovered/provided by the traffic generator self.intf_speed = 0 + else: + self.intf_speed = bitmath.parse_string(self.intf_speed.replace('ps', '')).bits self.name = gen_config.name self.zmq_pub_port = gen_config.get('zmq_pub_port', 4500) self.zmq_rpc_port = gen_config.get('zmq_rpc_port', 4501) @@ -713,13 +722,17 @@ class TrafficClient(object): # interface speed is overriden from config if self.intf_speed != tg_if_speed: # Warn the user if the speed in the config is different - LOG.warning('Interface speed provided is different from actual speed (%d Gbps)', - intf_speeds[0]) + LOG.warning( + 'Interface speed provided (%g Gbps) is different from actual speed (%d Gbps)', + self.intf_speed / 1000000000.0, intf_speeds[0]) else: # interface speed not provisioned by config self.intf_speed = tg_if_speed # also update the speed in the tg config self.generator_config.intf_speed = tg_if_speed + # let's report detected and actually used interface speed + self.config.intf_speed_detected = tg_if_speed + self.config.intf_speed_used = self.intf_speed # Save the traffic generator local MAC for mac, device in zip(self.gen.get_macs(), self.generator_config.devices): @@ -923,7 +936,9 @@ class TrafficClient(object): """Collect final stats for previous run.""" stats = self.gen.get_stats(self.ifstats) retDict = {'total_tx_rate': stats['total_tx_rate'], - 'offered_tx_rate_bps': stats['offered_tx_rate_bps']} + 'offered_tx_rate_bps': stats['offered_tx_rate_bps'], + 'theoretical_tx_rate_bps': stats['theoretical_tx_rate_bps'], + 'theoretical_tx_rate_pps': stats['theoretical_tx_rate_pps']} tx_keys = ['total_pkts', 'total_pkt_bytes', 'pkt_rate', 'pkt_bit_rate'] rx_keys = tx_keys + ['dropped_pkts'] diff --git a/nfvbench/traffic_gen/dummy.py b/nfvbench/traffic_gen/dummy.py index 25664e5..8a6d11a 100644 --- a/nfvbench/traffic_gen/dummy.py +++ b/nfvbench/traffic_gen/dummy.py @@ -151,6 +151,8 @@ class DummyTG(AbstractTrafficGenerator): avg_packet_size = utils.get_average_packet_size(self.l2_frame_size) total_tx_bps = utils.pps_to_bps(total_tx_pps, avg_packet_size) result['offered_tx_rate_bps'] = total_tx_bps + + result.update(self.get_theoretical_rates(avg_packet_size)) return result def get_stream_stats(self, tg_stats, if_stats, latencies, chain_idx): diff --git a/nfvbench/traffic_gen/traffic_base.py b/nfvbench/traffic_gen/traffic_base.py index abf5a22..df28772 100644 --- a/nfvbench/traffic_gen/traffic_base.py +++ b/nfvbench/traffic_gen/traffic_base.py @@ -15,6 +15,8 @@ import abc import sys +import bitmath + from nfvbench.log import LOG from . import traffic_utils @@ -126,3 +128,32 @@ class AbstractTrafficGenerator(object): return: a list of speed in Gbps indexed by the port# """ + + def get_theoretical_rates(self, avg_packet_size): + + result = {} + + intf_speeds = self.get_port_speed_gbps() + tg_if_speed = bitmath.parse_string(str(intf_speeds[0]) + 'Gb').bits + intf_speed = tg_if_speed + + if hasattr(self.config, 'intf_speed') and self.config.intf_speed is not None: + # in case of limitation due to config, TG speed is not accurate + # value is overridden by conf + if self.config.intf_speed != tg_if_speed: + intf_speed = bitmath.parse_string(self.config.intf_speed.replace('ps', '')).bits + + if hasattr(self.config, 'user_info') and self.config.user_info is not None: + if "extra_encapsulation_bytes" in self.config.user_info: + frame_size_full_encapsulation = avg_packet_size + self.config.user_info[ + "extra_encapsulation_bytes"] + result['theoretical_tx_rate_pps'] = traffic_utils.bps_to_pps( + intf_speed, frame_size_full_encapsulation) * 2 + result['theoretical_tx_rate_bps'] = traffic_utils.pps_to_bps( + result['theoretical_tx_rate_pps'], avg_packet_size) + else: + result['theoretical_tx_rate_pps'] = traffic_utils.bps_to_pps(intf_speed, + avg_packet_size) * 2 + result['theoretical_tx_rate_bps'] = traffic_utils.pps_to_bps( + result['theoretical_tx_rate_pps'], avg_packet_size) + return result diff --git a/nfvbench/traffic_gen/trex_gen.py b/nfvbench/traffic_gen/trex_gen.py index 0bf6d93..f7250da 100644 --- a/nfvbench/traffic_gen/trex_gen.py +++ b/nfvbench/traffic_gen/trex_gen.py @@ -159,6 +159,9 @@ class TRex(AbstractTrafficGenerator): avg_packet_size = utils.get_average_packet_size(self.l2_frame_size) total_tx_bps = utils.pps_to_bps(result["total_tx_rate"], avg_packet_size) result['offered_tx_rate_bps'] = total_tx_bps + + result.update(self.get_theoretical_rates(avg_packet_size)) + result["flow_stats"] = in_stats["flow_stats"] result["latency"] = in_stats["latency"] -- cgit 1.2.3-korg