summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/testing/user/userguide/advanced.rst60
-rw-r--r--docs/testing/user/userguide/faq.rst2
-rw-r--r--docs/testing/user/userguide/readme.rst13
-rw-r--r--nfvbench/cfg.default.yaml34
-rw-r--r--nfvbench/chain_runner.py2
-rw-r--r--nfvbench/nfvbench.py193
6 files changed, 227 insertions, 77 deletions
diff --git a/docs/testing/user/userguide/advanced.rst b/docs/testing/user/userguide/advanced.rst
index d7dc53f..2bd88cf 100644
--- a/docs/testing/user/userguide/advanced.rst
+++ b/docs/testing/user/userguide/advanced.rst
@@ -598,6 +598,66 @@ Used parameters:
* ``--unidir`` : run traffic with unidirectional flow (default to bidirectional flow)
+.. _adv-l2l-cli:
+
+L2 loopback test via CLI
+------------------------
+
+The CLI allows running a pure L2 loopback benchmark with the ``--l2-loopback`` option.
+Enabling this mode overrides any service chain type selected in the config file.
+The usual argument would be a single VLAN ID but the syntax has been extended.
+
+Examples of runs:
+
+* specify the vlan ID
+
+ .. code-block:: bash
+
+ nfvbench -c nfvbench.cfg --frame-size=64 --rate=100% --duration=10 --l2-loopback=123
+
+* specify a list of vlan IDs
+
+ Several service chains are created, the count is adjusted to the list size.
+
+ .. code-block:: bash
+
+ nfvbench -c nfvbench.cfg -fs=64 --rate=100% --duration=10 --l2-loopback=123,124,125
+
+* enable the mode without VLAN tagging
+
+ In this case the service chain count is forced to 1.
+
+ .. code-block:: bash
+
+ nfvbench -c nfvbench.cfg -fs=64 --rate=100% --duration=10 --l2-loopback=no-tag
+
+* use different VLAN tags for left & right side ports
+
+ .. code-block:: bash
+
+ nfvbench -c nfvbench.cfg -fs=64 --rate=100% --duration=10 --l2-loopback=111_211
+ nfvbench -c nfvbench.cfg -fs=64 --rate=100% --duration=10 --l2-loopback=111,112_211,212
+
+ .. note::
+ It may look bizarre to specify mismatched VLAN tags for left & right sides,
+ however no assumption is made about the loop implementation.
+ This could help testing some exotic L2 layer configuration comprising a VLAN rewriting.
+
+* enable the mode, starting from current settings (prepared in the cfg file)
+
+ In this case the service chain count is not adjusted.
+
+ .. code-block:: bash
+
+ nfvbench -c nfvbench.cfg -fs=64 --rate=100% --duration=10 --l2-loopback=true
+
+* disable the mode (possibly enabled in the cfg file)
+
+ .. code-block:: bash
+
+ nfvbench -c nfvbench.cfg --l2-loopback=false
+
+
MAC Addresses
-------------
diff --git a/docs/testing/user/userguide/faq.rst b/docs/testing/user/userguide/faq.rst
index a8aad9a..014a1ab 100644
--- a/docs/testing/user/userguide/faq.rst
+++ b/docs/testing/user/userguide/faq.rst
@@ -46,7 +46,7 @@ The most common issues that prevent traffic from passing are:
- incorrect vlan_tagging setting in the NFVbench configuration, this needs to match how the NFVbench ports on the switch are configured (trunk or access port)
- if the switch port is configured as access port, you must disable vlan_tagging in the NFVbench configuration
- - of the switch port is configured as trunk (recommended method), you must enable it
+ - if the switch port is configured as trunk (recommended method), you must enable it
Issues with high performances at a high line rate
-------------------------------------------------
diff --git a/docs/testing/user/userguide/readme.rst b/docs/testing/user/userguide/readme.rst
index 50175c3..f6cc153 100644
--- a/docs/testing/user/userguide/readme.rst
+++ b/docs/testing/user/userguide/readme.rst
@@ -78,15 +78,22 @@ NFVbench supports settings that involve externally staged packet paths with or w
Direct L2 Loopback (Switch or wire loopback)
--------------------------------------------
-NFVbench supports benchmarking of pure L2 loopbacks (see "--l2-loopback vlan" option)
+NFVbench supports benchmarking of pure L2 loopbacks
- Switch level loopback
- Port to port wire loopback
-In this mode, NFVbench will take a vlan ID and send packets from each port to the other port
-(dest MAC set to the other port MAC) using the same VLAN ID on both ports.
+In this mode, NFVbench will send packets from each port to the other port
+(the destination MAC address is set to the other port MAC address).
This can be useful for example to verify that the connectivity to the switch is working properly.
+Such a test can be quickly run using the CLI ``--l2-loopback`` :ref:`option <adv-l2l-cli>`.
+
+For a typical test, packets will be VLAN tagged with the same ID on both ports.
+However, multiple L2 vlan tagged service chains are also allowed,
+which permits testing various configurations and the behavior of the bench itself.
+
+
Traffic Generation
------------------
diff --git a/nfvbench/cfg.default.yaml b/nfvbench/cfg.default.yaml
index 26df919..4491097 100644
--- a/nfvbench/cfg.default.yaml
+++ b/nfvbench/cfg.default.yaml
@@ -60,7 +60,7 @@ flavor:
# Size of local disk in GB
disk: 0
# metadata are supported and can be added if needed, optional
- # note that if your openstack does not have NUMA optimization
+ # note that if your OpenStack does not have NUMA optimization
# (cpu pinning and huge pages)
# you must comment out extra_specs completely otherwise
# loopback VM creation will fail
@@ -79,7 +79,7 @@ flavor:
# When multiqueue is used the recommended setting is to set it to same value as the
# number of vCPU used - up to a max of 8 queues.
# Setting to a lower value than vCPU should also work. For example if using 4 vCPU and
-# vif_multiqueue_size is set to 2, openstack will create 4 queues per interface but the
+# vif_multiqueue_size is set to 2, OpenStack will create 4 queues per interface but the
# test VM will only use the first 2 queues.
vif_multiqueue_size: 1
@@ -95,7 +95,7 @@ num_mbufs: 16384
availability_zone:
# To force placement on a given hypervisor, set the name here
# (if multiple names are provided, the first will be used)
-# Leave empty to let openstack pick the hypervisor
+# Leave empty to let OpenStack pick the hypervisor
compute_nodes:
# If openrc is not admin set a valid value for hypervisor hostname
# Example of value: hypervisor_hostname: "server1"
@@ -128,10 +128,16 @@ flow_count: 10000
sriov: false
# Perform port to port loopback (direct or through switch)
-# Should be used with EXT service chain and no ARP (no_arp: true)
-# When enabled, the vlans property must contain the same VLAN id for all chains.
-# Can be overriden by --l2-loopback
+# e.g. for unitary testing of the switch or the bench itself.
+# When selected, this mode forces EXT service chain and no ARP mode
+# Destination MAC for each port is set to the other (peer) port MAC.
+# VLAN tagging is defined by 'vlans' & 'vlan_tagging' properties.
+# Can be overriden by --l2-loopback (including vlan tagging spec).
l2_loopback: false
+# No assumption is made about the loop implementation.
+# Multiple L2 vlan tagged service chains are allowed,
+# the vlan ID lists' size must be at least service_chain_count.
+# If not vlan tagging, the service chain count is forced to 1.
# Resources created by NFVbench will not be removed
# Can be overriden by --no-cleanup
@@ -390,7 +396,7 @@ generic_poll_sec: 2
# name of the loop VM
loop_vm_name: 'nfvbench-loop-vm'
-# Default names, subnets and CIDRs for PVP/PVVP networks (openstack only)
+# Default names, subnets and CIDRs for PVP/PVVP networks (OpenStack only)
#
# If a network with given name already exists it will be reused.
# - PVP only uses left and right
@@ -673,19 +679,21 @@ mpls: false
# is not supported). Use the vtep_vlan option to enable vlan tagging for the VxLAN overlay network.
vlan_tagging: true
-# Used only in the case of EXT chain and no openstack or not admin access to specify the VLAN IDs to use.
-# This property is ignored when OpenStakc is used or in the case of l2-loopback.
+# Used only in the case of EXT chain and no OpenStack or not admin access to specify the VLAN IDs to use.
+# This property is ignored when OpenStack is used or when 'vlan_tagging' is disabled.
# If OpenStack is used leave the list empty, VLAN IDs are retrieved from OpenStack networks using Neutron API.
# If networks are shared across all chains (service_chain_shared_net=true), the list should have exactly 2 values
# If networks are not shared across chains (service_chain_shared_net=false), the list should have
# 2 list of vlan IDs
-# In the special case of l2-loopback the list should have the same VLAN id for all chains
# Examples:
# [1998, 1999] left network uses vlan 1998 right network uses vlan 1999
# [[1,2],[3,4]] chain 0 left vlan 1, right vlan 2 - chain 1 left vlan 3 right vlan 4
-# [1010, 1010] same VLAN id with l2-loopback enabled
-#
+# [1010, 1010] same vlan ID on both sides, for a typical l2-loopback test (*)
+# The vlan lists may be oversized, compared to the actual service chain count
+# (lowest indexes are used) but an exception is raised if they are too short.
vlans: []
+# (*) actually there is no restriction, left/right IDs may differ
+# for some exotic purpose - see also the l2_loopback parameter.
# ARP is used to discover the MAC address of VNFs that run L3 routing.
# Used only with EXT chain.
@@ -725,7 +733,7 @@ traffic:
# Can be overriden by --no-traffic
no_traffic: false
-# Use an L3 router in the packet path. This option if set will create or reuse an openstack neutron
+# Use an L3 router in the packet path. This option if set will create or reuse an OpenStack neutron
# router (PVP, PVVP) or reuse an existing L3 router (EXT) to route traffic to the destination VM.
# Can be overriden by --l3-router
l3_router: false
diff --git a/nfvbench/chain_runner.py b/nfvbench/chain_runner.py
index ae9ccff..7b1153f 100644
--- a/nfvbench/chain_runner.py
+++ b/nfvbench/chain_runner.py
@@ -71,6 +71,8 @@ class ChainRunner(object):
# VLAN is discovered from the networks
gen_config.set_vlans(0, self.chain_manager.get_chain_vlans(0))
gen_config.set_vlans(1, self.chain_manager.get_chain_vlans(1))
+ else:
+ LOG.info("Ports: untagged")
# the only case we do not need to set the dest MAC is in the case of
# l2-loopback (because the traffic gen will default to use the peer MAC)
diff --git a/nfvbench/nfvbench.py b/nfvbench/nfvbench.py
index a178d24..427c94c 100644
--- a/nfvbench/nfvbench.py
+++ b/nfvbench/nfvbench.py
@@ -199,12 +199,23 @@ class NFVBench(object):
config.service_chain = config.service_chain.upper()
config.service_chain_count = int(config.service_chain_count)
if config.l2_loopback:
- # force the number of chains to be 1 in case of l2 loopback
- config.service_chain_count = 1
+ # force the number of chains to be 1 in case of untagged l2 loopback
+ # (on the other hand, multiple L2 vlan tagged service chains are allowed)
+ if not config.vlan_tagging:
+ config.service_chain_count = 1
config.service_chain = ChainType.EXT
config.no_arp = True
LOG.info('Running L2 loopback: using EXT chain/no ARP')
+ # allow oversized vlan lists, just clip them
+ try:
+ vlans = [list(v) for v in config.vlans]
+ for v in vlans:
+ del v[config.service_chain_count:]
+ config.vlans = vlans
+ except Exception:
+ pass
+
# traffic profile override options
if 'frame_sizes' in opts:
unidir = False
@@ -498,11 +509,13 @@ def _parse_opts_from_cli():
parser.add_argument('--l2-loopback', '--l2loopback', dest='l2_loopback',
action='store',
- metavar='<vlan>',
- help='Port to port or port to switch to port L2 loopback with VLAN id')
+ metavar='<vlan(s)|no-tag|true|false>',
+ help='Port to port or port to switch to port L2 loopback '
+ 'tagged with given VLAN id(s) or not (given \'no-tag\') '
+ '\'true\': use current vlans; \'false\': disable this mode.')
parser.add_argument('--user-info', dest='user_info',
- action='store',
+ action='append',
metavar='<data>',
help='Custom data to be included as is '
'in the json report config branch - '
@@ -536,32 +549,32 @@ def _parse_opts_from_cli():
type=int_arg,
metavar='<size>',
action='store',
- default='0',
+ default=None,
help='Specify the FE cache size (default: 0, flow-count if < 0)')
parser.add_argument('--service-mode', dest='service_mode',
action='store_true',
- default=False,
+ default=None,
help='Enable T-Rex service mode (for debugging purpose)')
parser.add_argument('--no-e2e-check', dest='no_e2e_check',
action='store_true',
- default=False,
+ default=None,
help='Skip "end to end" connectivity check (on test purpose)')
parser.add_argument('--no-flow-stats', dest='no_flow_stats',
action='store_true',
- default=False,
+ default=None,
help='Disable additional flow stats (on high load traffic)')
parser.add_argument('--no-latency-stats', dest='no_latency_stats',
action='store_true',
- default=False,
+ default=None,
help='Disable flow stats for latency traffic')
parser.add_argument('--no-latency-streams', dest='no_latency_streams',
action='store_true',
- default=False,
+ default=None,
help='Disable latency measurements (no streams)')
parser.add_argument('--user-id', dest='user_id',
@@ -726,65 +739,125 @@ 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)
- # copy over cli options that are used in config
+ # Copy over some of the cli options that are used in config.
+ # This explicit copy is sometimes necessary
+ # because some early evaluation depends on them
+ # and cannot wait for _update_config() coming further.
+ # It is good practice then to set them to None (<=> done)
+ # and even required if a specific conversion is performed here
+ # that would be corrupted by a default update (simple copy).
+ # On the other hand, some excessive assignments have been removed
+ # from here, since the _update_config() procedure does them well.
+
config.generator_profile = opts.generator_profile
- if opts.sriov:
+ if opts.sriov is not None:
config.sriov = True
- if opts.log_file:
+ opts.sriov = None
+ if opts.log_file is not None:
config.log_file = opts.log_file
- if opts.service_chain:
+ opts.log_file = None
+ if opts.user_id is not None:
+ config.user_id = opts.user_id
+ opts.user_id = None
+ if opts.group_id is not None:
+ config.group_id = opts.group_id
+ opts.group_id = None
+ if opts.service_chain is not None:
config.service_chain = opts.service_chain
- if opts.service_chain_count:
- config.service_chain_count = opts.service_chain_count
- if opts.no_vswitch_access:
- config.no_vswitch_access = opts.no_vswitch_access
- if opts.hypervisor:
+ opts.service_chain = None
+ if opts.hypervisor is not None:
# can be any of 'comp1', 'nova:', 'nova:comp1'
config.compute_nodes = opts.hypervisor
- if opts.vxlan:
- config.vxlan = True
- if opts.mpls:
- config.mpls = True
- if opts.restart:
- config.restart = True
- if opts.service_mode:
- config.service_mode = True
- if opts.no_flow_stats:
- config.no_flow_stats = True
- if opts.no_latency_stats:
- config.no_latency_stats = True
- if opts.no_latency_streams:
- config.no_latency_streams = True
- # port to port loopback (direct or through switch)
- if opts.l2_loopback:
- config.l2_loopback = True
- if config.service_chain != ChainType.EXT:
- LOG.info('Changing service chain type to EXT')
- config.service_chain = ChainType.EXT
- if not config.no_arp:
- LOG.info('Disabling ARP')
- config.no_arp = True
- config.vlans = [int(opts.l2_loopback), int(opts.l2_loopback)]
- LOG.info('Running L2 loopback: using EXT chain/no ARP')
+ opts.hypervisor = None
+ if opts.debug_mask is not None:
+ config.debug_mask = opts.debug_mask
+ opts.debug_mask = None
- if opts.use_sriov_middle_net:
- if (not config.sriov) or (config.service_chain != ChainType.PVVP):
- raise Exception("--use-sriov-middle-net is only valid for PVVP with SRIOV")
- config.use_sriov_middle_net = True
+ # convert 'user_info' opt from json string to dictionnary
+ # and merge the result with the current config dictionnary
+ if opts.user_info is not None:
+ for user_info_json in opts.user_info:
+ user_info_dict = json.loads(user_info_json)
+ if config.user_info:
+ config.user_info = config.user_info + user_info_dict
+ else:
+ config.user_info = user_info_dict
+ opts.user_info = None
+
+ # port to port loopback (direct or through switch)
+ # we accept the following syntaxes for the CLI argument
+ # 'false' : mode not enabled
+ # 'true' : mode enabled with currently defined vlan IDs
+ # 'no-tag' : mode enabled with no vlan tagging
+ # <vlan IDs>: mode enabled using the given (pair of) vlan ID lists
+ # - If present, a '_' char will separate left an right ports lists
+ # e.g. 'a_x' => vlans: [[a],[x]]
+ # 'a,b,c_x,y,z' => [[a,b,c],[x,y,z]]
+ # - Otherwise the given vlan ID list applies to both sides
+ # e.g. 'a' => vlans: [[a],[a]]
+ # 'a,b' => [[a,b],[a,b]]
+ # - Vlan lists size needs to be at least the actual SCC value
+ # - Unless overriden in CLI opts, config.service_chain_count
+ # is adjusted to the size of the VLAN ID lists given here.
+
+ if opts.l2_loopback is not None:
+ arg_pair = opts.l2_loopback.lower().split('_')
+ if arg_pair[0] == 'false':
+ config.l2_loopback = False
+ else:
+ config.l2_loopback = True
+ if config.service_chain != ChainType.EXT:
+ LOG.info('Changing service chain type to EXT')
+ config.service_chain = ChainType.EXT
+ if not config.no_arp:
+ LOG.info('Disabling ARP')
+ config.no_arp = True
+ if arg_pair[0] == 'true':
+ pass
+ else:
+ # here explicit (not)tagging is not CLI overridable
+ opts.vlan_tagging = None
+ if arg_pair[0] == 'no-tag':
+ config.vlan_tagging = False
+ else:
+ config.vlan_tagging = True
+ if len(arg_pair) == 1 or not arg_pair[1]:
+ arg_pair = [arg_pair[0], arg_pair[0]]
+ vlans = [[], []]
+
+ def append_vlan(port, vlan_id):
+ # a vlan tag value must be in [0..4095]
+ if vlan_id not in range(0, 4096):
+ raise ValueError
+ vlans[port].append(vlan_id)
+ try:
+ for port in [0, 1]:
+ vlan_ids = arg_pair[port].split(',')
+ for vlan_id in vlan_ids:
+ append_vlan(port, int(vlan_id))
+ if len(vlans[0]) != len(vlans[1]):
+ raise ValueError
+ except ValueError:
+ # at least one invalid tag => no tagging
+ config.vlan_tagging = False
+ if config.vlan_tagging:
+ config.vlans = vlans
+ # force service chain count if not CLI overriden
+ if opts.service_chain_count is None:
+ config.service_chain_count = len(vlans[0])
+ opts.l2_loopback = None
+
+ if config.use_sriov_middle_net is None:
+ config.use_sriov_middle_net = False
+ if opts.use_sriov_middle_net is not None:
+ config.use_sriov_middle_net = opts.use_sriov_middle_net
+ opts.use_sriov_middle_net = None
+ if (config.use_sriov_middle_net and (
+ (not config.sriov) or (config.service_chain != ChainType.PVVP))):
+ raise Exception("--use-sriov-middle-net is only valid for PVVP with SRIOV")
if config.sriov and config.service_chain != ChainType.EXT:
# if sriov is requested (does not apply to ext chains)