NFVBENCH-187: Augment --l2-loopback command line option capabilities 28/71328/5
authorPierrick Louin <pierrick.louin@orange.com>
Sun, 8 Nov 2020 21:31:41 +0000 (22:31 +0100)
committerPierrick Louin <pierrick.louin@orange.com>
Mon, 9 Nov 2020 09:37:30 +0000 (10:37 +0100)
[vlan(s)|no-tag|true|false] - Update documentation
Clarify some fuzzy coding in options processing [nfvbench.py]

Change-Id: Ie6eec7722bfa557924f435f268b852c300e160df
Signed-off-by: Pierrick Louin <pierrick.louin@orange.com>
docs/testing/user/userguide/advanced.rst
docs/testing/user/userguide/faq.rst
docs/testing/user/userguide/readme.rst
nfvbench/cfg.default.yaml
nfvbench/chain_runner.py
nfvbench/credentials.py
nfvbench/nfvbench.py
nfvbench/traffic_gen/trex_gen.py
nfvbench/traffic_server.py

index d7dc53f..2bd88cf 100644 (file)
@@ -598,6 +598,66 @@ Used parameters:
 * ``--unidir`` : run traffic with unidirectional flow (default to bidirectional flow)
 
 
 * ``--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
 -------------
 
 MAC Addresses
 -------------
 
index a8aad9a..014a1ab 100644 (file)
@@ -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
 - 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
 -------------------------------------------------
 
 Issues with high performances at a high line rate
 -------------------------------------------------
index 50175c3..f6cc153 100644 (file)
@@ -78,15 +78,22 @@ NFVbench supports settings that involve externally staged packet paths with or w
 
 Direct L2 Loopback (Switch or wire loopback)
 --------------------------------------------
 
 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
 
 
 - 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.
 
 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
 ------------------
 
 Traffic Generation
 ------------------
 
index 26df919..4491097 100644 (file)
@@ -60,7 +60,7 @@ flavor:
   # Size of local disk in GB
   disk: 0
   # metadata are supported and can be added if needed, optional
   # 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
   # (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
 # 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
 
 # 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)
 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"
 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)
 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
 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
 
 # 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'
 
 # 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
 #
 # 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
 
 # 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
 # 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
 # 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: []
 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.
 
 # 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
 
 # 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
 # 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
index ae9ccff..7b1153f 100644 (file)
@@ -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))
             # 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)
 
         # 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)
index d9a67e6..4e4985f 100644 (file)
@@ -176,7 +176,7 @@ class Credentials(object):
             # Return HTTP 200 if user is admin
             self.get_session().get('/users', endpoint_filter=filter)
             self.is_admin = True
             # Return HTTP 200 if user is admin
             self.get_session().get('/users', endpoint_filter=filter)
             self.is_admin = True
-        except Exception as e:
+        except Exception:
             try:
                 # vX/users URL returns exception (HTTP 403) if user is not admin.
                 self.get_session().get('/v' + str(self.rc_identity_api_version) + '/users',
             try:
                 # vX/users URL returns exception (HTTP 403) if user is not admin.
                 self.get_session().get('/v' + str(self.rc_identity_api_version) + '/users',
index 19c402f..427c94c 100644 (file)
@@ -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:
         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')
 
             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
         # traffic profile override options
         if 'frame_sizes' in opts:
             unidir = False
@@ -498,16 +509,20 @@ def _parse_opts_from_cli():
 
     parser.add_argument('--l2-loopback', '--l2loopback', dest='l2_loopback',
                         action='store',
 
     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',
 
     parser.add_argument('--user-info', dest='user_info',
-                        action='store',
+                        action='append',
                         metavar='<data>',
                         metavar='<data>',
-                        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}}\'')
+                        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}}\' - '
+                             'this option may be repeated; given data will be merged.')
 
     parser.add_argument('--vlan-tagging', dest='vlan_tagging',
                         type=bool_arg,
 
     parser.add_argument('--vlan-tagging', dest='vlan_tagging',
                         type=bool_arg,
@@ -521,7 +536,7 @@ def _parse_opts_from_cli():
                         action='store',
                         default=None,
                         help='Override the NFVbench \'intf_speed\' '
                         action='store',
                         default=None,
                         help='Override the NFVbench \'intf_speed\' '
-                                + 'parameter (e.g. 10Gbps, auto, 16.72Gbps)')
+                             'parameter (e.g. 10Gbps, auto, 16.72Gbps)')
 
     parser.add_argument('--cores', dest='cores',
                         type=int_arg,
 
     parser.add_argument('--cores', dest='cores',
                         type=int_arg,
@@ -534,32 +549,32 @@ def _parse_opts_from_cli():
                         type=int_arg,
                         metavar='<size>',
                         action='store',
                         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',
                         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',
                         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',
                         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',
                         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',
                         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',
                         help='Disable latency measurements (no streams)')
 
     parser.add_argument('--user-id', dest='user_id',
@@ -580,16 +595,16 @@ def _parse_opts_from_cli():
                         default=None,
                         action='store_true',
                         help='Show the current TRex local server log file contents'
                         default=None,
                         action='store_true',
                         help='Show the current TRex local server log file contents'
-                               + ' => diagnostic/help in case of configuration problems')
+                             ' => diagnostic/help in case of configuration problems')
 
     parser.add_argument('--debug-mask', dest='debug_mask',
                         type=int_arg,
                         metavar='<mask>',
                         action='store',
 
     parser.add_argument('--debug-mask', dest='debug_mask',
                         type=int_arg,
                         metavar='<mask>',
                         action='store',
-                        default='0x00000000',
+                        default=None,
                         help='General purpose register (debugging flags), '
                         help='General purpose register (debugging flags), '
-                                + 'the hexadecimal notation (0x...) is accepted.'
-                                + 'Designed for development needs.')
+                             'the hexadecimal notation (0x...) is accepted.'
+                             'Designed for development needs (default: 0).')
 
     opts, unknown_opts = parser.parse_known_args()
     return opts, unknown_opts
 
     opts, unknown_opts = parser.parse_known_args()
     return opts, unknown_opts
@@ -724,65 +739,125 @@ def main():
                 LOG.addHandler(fluent_logger)
                 break
 
                 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)
 
         # 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
         config.generator_profile = opts.generator_profile
-        if opts.sriov:
+        if opts.sriov is not None:
             config.sriov = True
             config.sriov = True
-        if opts.log_file:
+            opts.sriov = None
+        if opts.log_file is not None:
             config.log_file = opts.log_file
             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
             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
             # 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)
 
         if config.sriov and config.service_chain != ChainType.EXT:
             # if sriov is requested (does not apply to ext chains)
index 4e20f73..21e79ae 100644 (file)
@@ -589,8 +589,6 @@ class TRex(AbstractTrafficGenerator):
         """
         streams = []
         pg_id, lat_pg_id = self.get_pg_id(port, chain_id)
         """
         streams = []
         pg_id, lat_pg_id = self.get_pg_id(port, chain_id)
-        if self.config.no_flow_stats:
-            LOG.info("Traffic flow statistics are disabled.")
         if l2frame == 'IMIX':
             for ratio, l2_frame_size in zip(IMIX_RATIOS, IMIX_L2_SIZES):
                 pkt = self._create_pkt(stream_cfg, l2_frame_size)
         if l2frame == 'IMIX':
             for ratio, l2_frame_size in zip(IMIX_RATIOS, IMIX_L2_SIZES):
                 pkt = self._create_pkt(stream_cfg, l2_frame_size)
@@ -602,12 +600,12 @@ class TRex(AbstractTrafficGenerator):
                         streams.append(STLStream(packet=pkt,
                                                  flow_stats=STLFlowStats(pg_id=pg_id,
                                                                          vxlan=True)
                         streams.append(STLStream(packet=pkt,
                                                  flow_stats=STLFlowStats(pg_id=pg_id,
                                                                          vxlan=True)
-                                                 if not self.config.no_flow_stats else None,
+                                                    if not self.config.no_flow_stats else None,
                                                  mode=STLTXCont(pps=ratio)))
                     else:
                         streams.append(STLStream(packet=pkt,
                                                  flow_stats=STLFlowStats(pg_id=pg_id)
                                                  mode=STLTXCont(pps=ratio)))
                     else:
                         streams.append(STLStream(packet=pkt,
                                                  flow_stats=STLFlowStats(pg_id=pg_id)
-                                                 if not self.config.no_flow_stats else None,
+                                                    if not self.config.no_flow_stats else None,
                                                  mode=STLTXCont(pps=ratio)))
 
             if latency:
                                                  mode=STLTXCont(pps=ratio)))
 
             if latency:
@@ -633,12 +631,12 @@ class TRex(AbstractTrafficGenerator):
                     streams.append(STLStream(packet=pkt,
                                              flow_stats=STLFlowStats(pg_id=pg_id,
                                                                      vxlan=True)
                     streams.append(STLStream(packet=pkt,
                                              flow_stats=STLFlowStats(pg_id=pg_id,
                                                                      vxlan=True)
-                                             if not self.config.no_flow_stats else None,
+                                                if not self.config.no_flow_stats else None,
                                              mode=STLTXCont()))
                 else:
                     streams.append(STLStream(packet=pkt,
                                              flow_stats=STLFlowStats(pg_id=pg_id)
                                              mode=STLTXCont()))
                 else:
                     streams.append(STLStream(packet=pkt,
                                              flow_stats=STLFlowStats(pg_id=pg_id)
-                                             if not self.config.no_flow_stats else None,
+                                                if not self.config.no_flow_stats else None,
                                              mode=STLTXCont()))
             # for the latency stream, the minimum payload is 16 bytes even in case of vlan tagging
             # without vlan, the min l2 frame size is 64
                                              mode=STLTXCont()))
             # for the latency stream, the minimum payload is 16 bytes even in case of vlan tagging
             # without vlan, the min l2 frame size is 64
@@ -662,12 +660,12 @@ class TRex(AbstractTrafficGenerator):
                 streams.append(STLStream(packet=pkt,
                                          flow_stats=STLFlowLatencyStats(pg_id=lat_pg_id,
                                                                         vxlan=True)
                 streams.append(STLStream(packet=pkt,
                                          flow_stats=STLFlowLatencyStats(pg_id=lat_pg_id,
                                                                         vxlan=True)
-                                         if not self.config.no_latency_stats else None,
+                                            if not self.config.no_latency_stats else None,
                                          mode=STLTXCont(pps=self.LATENCY_PPS)))
             else:
                 streams.append(STLStream(packet=pkt,
                                          flow_stats=STLFlowLatencyStats(pg_id=lat_pg_id)
                                          mode=STLTXCont(pps=self.LATENCY_PPS)))
             else:
                 streams.append(STLStream(packet=pkt,
                                          flow_stats=STLFlowLatencyStats(pg_id=lat_pg_id)
-                                         if not self.config.no_latency_stats else None,
+                                            if not self.config.no_latency_stats else None,
                                          mode=STLTXCont(pps=self.LATENCY_PPS)))
         return streams
 
                                          mode=STLTXCont(pps=self.LATENCY_PPS)))
         return streams
 
@@ -1005,6 +1003,8 @@ class TRex(AbstractTrafficGenerator):
         latency: True if latency measurement is needed
         e2e: True if performing "end to end" connectivity check
         """
         latency: True if latency measurement is needed
         e2e: True if performing "end to end" connectivity check
         """
+        if self.config.no_flow_stats:
+            LOG.info("Traffic flow statistics are disabled.")
         r = self.__is_rate_enough(l2frame_size, rates, bidirectional, latency)
         if not r['result']:
             raise TrafficGeneratorException(
         r = self.__is_rate_enough(l2frame_size, rates, bidirectional, latency)
         if not r['result']:
             raise TrafficGeneratorException(
index bc79204..6074a6e 100644 (file)
@@ -108,8 +108,8 @@ class TRexTrafficServer(TrafficServer):
                                          prefix=generator_config.name,
                                          limit_memory=generator_config.limit_memory,
                                          nb_cores=generator_config.cores,
                                          prefix=generator_config.name,
                                          limit_memory=generator_config.limit_memory,
                                          nb_cores=generator_config.cores,
-                                         use_vlan=generator_config.gen_config.get('vtep_vlan')
-                                         or generator_config.vlan_tagging,
+                                         use_vlan=generator_config.gen_config.get('vtep_vlan') or
+                                         generator_config.vlan_tagging,
                                          ifs=ifs)
 
         if hasattr(generator_config, 'mbuf_64') and generator_config.mbuf_64:
                                          ifs=ifs)
 
         if hasattr(generator_config, 'mbuf_64') and generator_config.mbuf_64: