integration: Support for VxLAN TC without using overlay traffic gen. 17/17417/2
authorSugesh Chandran <sugesh.chandran@intel.com>
Thu, 7 Jul 2016 09:24:36 +0000 (10:24 +0100)
committerMaryam Tahhan <maryam.tahhan@intel.com>
Tue, 16 Aug 2016 15:12:11 +0000 (15:12 +0000)
The test case for verifying the vxlan tunneling feature without using any
ingress tunnel traffic. The virtual switch(OVS) is configured to mangle and
generate tunnel traffic in the deployment. The packet flow in the test case is
as follows

TRAFFIC-IN --> [ENCAP-PKT] --> [MOD-PKT] --> [DECAP-PKT] --> TRAFFIC-OUT

 ENCAP-PKT - Encapsulate the ingress packet to a tunnel type.
 MOD-PKT   - Modify the tunnel header to match the following decap interface.
 DECAP-PKT - Decapsulate the newly generated tunneled packet.

Change-Id: Ie24bacb3cb1c069bd60403e5a4ef8bcdf0e12e54
Signed-off-by: Sugesh Chandran <sugesh.chandran@intel.com>
conf/integration/01_testcases.conf
conf/integration/02_vswitch.conf
core/component_factory.py
core/vswitch_controller_ptunp.py [new file with mode: 0644]
docs/userguide/integration.rst
vswitches/ovs.py

index e050e35..4631263 100644 (file)
@@ -132,6 +132,17 @@ STEP_VSWITCH_PVVP_FLOWS_FINIT = [
 # Definition of integration tests
 #
 INTEGRATION_TESTS = [
+    {
+        "Name": "overlay_p2p_mod_tput",
+        "Traffic Type": "rfc2544",
+        "Deployment": "ptunp",
+        "biDirectional": 'True',
+        "Tunnel Type": "vxlan",
+        "Description": ("Tunneling Throughput RFC2544 Test."
+                       "The encap and decap are performed inside the "
+                       "virtual switch itself in each direction to avoid "
+                       "the need of ingress overlay traffic."),
+    },
     {
         "Name": "overlay_p2p_tput",
         "Traffic Type": "rfc2544",
index 6bdf79d..68eaf73 100644 (file)
@@ -28,3 +28,15 @@ TUNNEL_TYPE = 'vxlan'
 DUT_NIC1_MAC = ''
 # Used for OVS DPDK Decap
 DUT_NIC2_MAC = ''
+
+#Tunnel bridge configuration for P-TUN-P(VxLAN) deployment scenario
+# to test VxLAN performance without any overlay ingress traffic by doing the
+# encap decap inside the virtual switch itself.
+TUNNEL_EXTERNAL_BRIDGE1 = 'br-phy1'
+TUNNEL_EXTERNAL_BRIDGE2 = 'br-phy2'
+TUNNEL_MODIFY_BRIDGE1 = 'br-mod1'
+TUNNEL_MODIFY_BRIDGE2 = 'br-mod2'
+TUNNEL_MODIFY_BRIDGE_MAC1 = '00:00:10:00:00:01'
+TUNNEL_MODIFY_BRIDGE_MAC2 = '00:00:20:00:00:01'
+TUNNEL_MODIFY_BRIDGE_IP1 = '10.0.0.1/24'
+TUNNEL_MODIFY_BRIDGE_IP2 = '20.0.0.1/24'
index a91872e..258b723 100644 (file)
@@ -21,6 +21,7 @@ from core.vswitch_controller_p2p import VswitchControllerP2P
 from core.vswitch_controller_pvp import VswitchControllerPVP
 from core.vswitch_controller_pvvp import VswitchControllerPVVP
 from core.vswitch_controller_op2p import VswitchControllerOP2P
+from core.vswitch_controller_ptunp import VswitchControllerPtunP
 from core.vnf_controller import VnfController
 from core.pktfwd_controller import PktFwdController
 from tools.load_gen.stress.stress import Stress
@@ -73,6 +74,8 @@ def create_vswitch(deployment_scenario, vswitch_class, traffic,
         return VswitchControllerPVVP(vswitch_class, traffic)
     elif deployment_scenario.find("op2p") >= 0:
         return VswitchControllerOP2P(vswitch_class, traffic, tunnel_operation)
+    elif deployment_scenario.find("ptunp") >= 0:
+        return VswitchControllerPtunP(vswitch_class, traffic)
     elif deployment_scenario.find("clean") >= 0:
         return VswitchControllerClean(vswitch_class, traffic)
 
diff --git a/core/vswitch_controller_ptunp.py b/core/vswitch_controller_ptunp.py
new file mode 100644 (file)
index 0000000..27d2678
--- /dev/null
@@ -0,0 +1,238 @@
+# Copyright 2015-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.
+
+"""VSwitch controller for Physical to VxLAN Tunnel Endpoint to Physical
+   deployment with mod operation.
+"""
+
+import logging
+from netaddr import EUI, IPNetwork, mac_unix
+
+from core.vswitch_controller import IVswitchController
+from vswitches.utils import add_ports_to_flow
+from conf import settings
+from tools import tasks
+
+_FLOW_TEMPLATE = {
+    'idle_timeout': '0'
+}
+
+class VswitchControllerPtunP(IVswitchController):
+    """VSwitch controller for VxLAN ptunp deployment scenario.
+    The deployment scenario is to test VxLAN tunneling feature without using an
+    overlay ingress traffic. The VxLAN encap and decap performed in the virtual
+    switch in each direction.
+
+    Attributes:
+        _vswitch_class: The vSwitch class to be used.
+        _vswitch: The vSwitch object controlled by this controller
+        _deployment_scenario: A string describing the scenario to set-up in the
+            constructor.
+    """
+    def __init__(self, vswitch_class, traffic):
+        """Initializes up the prerequisites for the ptunp deployment scenario.
+
+        :vswitch_class: the vSwitch class to be used.
+        """
+        self._logger = logging.getLogger(__name__)
+        self._vswitch_class = vswitch_class
+        self._vswitch = vswitch_class()
+        self._deployment_scenario = "ptunp"
+        self._traffic = traffic.copy()
+        self.bridge_phy1 = settings.getValue('TUNNEL_EXTERNAL_BRIDGE1')
+        self.bridge_phy2 = settings.getValue('TUNNEL_EXTERNAL_BRIDGE2')
+        self.bridge_mod1 = settings.getValue('TUNNEL_MODIFY_BRIDGE1')
+        self.bridge_mod2 = settings.getValue('TUNNEL_MODIFY_BRIDGE2')
+        self.br_mod_mac1 = settings.getValue('TUNNEL_MODIFY_BRIDGE_MAC1')
+        self.br_mod_mac2 = settings.getValue('TUNNEL_MODIFY_BRIDGE_MAC2')
+        self.br_mod_ip1 = settings.getValue('TUNNEL_MODIFY_BRIDGE_IP1')
+        self.br_mod_ip2 = settings.getValue('TUNNEL_MODIFY_BRIDGE_IP2')
+        self.tunnel_type = settings.getValue('TUNNEL_TYPE')
+        self._logger.debug('Creation using ' + str(self._vswitch_class))
+
+    def setup(self):
+        """ Sets up the switch for VxLAN overlay PTUNP (tunnel encap or decap)
+        """
+        self._logger.debug('Setting up phy-tun-phy tunneling scenario')
+        if self.tunnel_type is 'vxlan':
+            self._setup_vxlan_encap_decap()
+        else:
+            self._logger.error("Only VxLAN is supported for now")
+            raise NotImplementedError
+
+    def _setup_vxlan_encap_decap(self):
+        """ Sets up switches for VxLAN overlay P-TUN-P test.
+
+            Create 2 bridges br-phy1 and br-phy2 (The bridge to connect
+            physical ports. Two more bridges br-mod1 and br-mod2 to mangle
+            and redirect the packets from one tunnel port to other.
+        """
+        self._logger.debug('Setup using ' + str(self._vswitch_class))
+        try:
+            self._vswitch.start()
+            self._vswitch.add_switch(self.bridge_phy1)
+            self._vswitch.add_switch(self.bridge_phy2)
+            self._vswitch.add_switch(self.bridge_mod1,
+                                     params=["other_config:hwaddr=" +
+                                             self.br_mod_mac1
+                                            ])
+            self._vswitch.add_switch(self.bridge_mod2,
+                                     params=["other_config:hwaddr=" +
+                                             self.br_mod_mac2
+                                            ])
+
+            tasks.run_task(['sudo', 'iptables', '-F'],
+                           self._logger, 'Clean ip tables',
+                           False)
+            tasks.run_task(['sudo', 'ip', 'addr', 'add',
+                            self.br_mod_ip1, 'dev', self.bridge_mod1],
+                           self._logger, 'Assign ' +
+                           self.br_mod_ip1 + ' to ' + self.bridge_mod1,
+                           False)
+            tasks.run_task(['sudo', 'ip', 'link', 'set', 'dev', self.bridge_mod1, 'up'],
+                           self._logger, 'Bring up ' + self.bridge_mod1, False)
+
+            tasks.run_task(['sudo', 'ip', 'addr', 'add',
+                            self.br_mod_ip2, 'dev', self.bridge_mod2],
+                           self._logger, 'Assign ' +
+                           self.br_mod_ip2 + ' to ' + self.bridge_mod2,
+                           False)
+            tasks.run_task(['sudo', 'ip', 'link', 'set', 'dev', self.bridge_mod2, 'up'],
+                           self._logger, 'Bring up ' + self.bridge_mod2, False)
+
+            tasks.run_task(['sudo', 'ip', 'link', 'set', 'dev', self.bridge_phy1, 'up'],
+                           self._logger, 'Bring up ' + self.bridge_phy1, False)
+            tasks.run_task(['sudo', 'ip', 'link', 'set', 'dev', self.bridge_phy2, 'up'],
+                           self._logger, 'Bring up ' + self.bridge_phy2, False)
+            self._vswitch.add_route(self.bridge_phy1, self.br_mod_ip1, self.bridge_mod1)
+            self._vswitch.add_route(self.bridge_phy2, self.br_mod_ip2, self.bridge_mod2)
+
+            # Create tunnel ip and mac from the bridge ips
+            vxlan_local_ip1 = str(IPNetwork(self.br_mod_ip1).ip)
+            vxlan_local_ip2 = str(IPNetwork(self.br_mod_ip2).ip)
+            vxlan_rem_ip1 = str(IPNetwork(self.br_mod_ip1).ip + 1)
+            vxlan_rem_ip2 = str(IPNetwork(self.br_mod_ip2).ip + 1)
+            vxlan_rem_mac1 = EUI(int(EUI(self.br_mod_mac1)) + 1)
+            vxlan_rem_mac1.dialect = mac_unix
+            vxlan_rem_mac2 = EUI(int(EUI(self.br_mod_mac2)) + 1)
+            vxlan_rem_mac2.dialect = mac_unix
+            self._vswitch.set_tunnel_arp(vxlan_local_ip1, self.br_mod_mac1,
+                                         self.bridge_phy1)
+            self._vswitch.set_tunnel_arp(vxlan_local_ip2, self.br_mod_mac2,
+                                         self.bridge_phy2)
+            self._vswitch.set_tunnel_arp(vxlan_rem_ip1, str(vxlan_rem_mac1),
+                                         self.bridge_mod1)
+            self._vswitch.set_tunnel_arp(vxlan_rem_ip2, str(vxlan_rem_mac2),
+                                         self.bridge_mod2)
+
+            # Lets add the ports to bridges
+            (_, phy1_number) = self._vswitch.add_phy_port(self.bridge_phy1)
+            (_, phy2_number) = self._vswitch.add_phy_port(self.bridge_phy2)
+            vxlan_vni = 'options:key=' + settings.getValue('VXLAN_VNI')
+            (_, phy3_number) = self._vswitch.add_tunnel_port(self.bridge_phy1,
+                                                             vxlan_rem_ip1,
+                                                             "vxlan",
+                                                             params=[vxlan_vni])
+            (_, phy4_number) = self._vswitch.add_tunnel_port(self.bridge_phy2,
+                                                             vxlan_rem_ip2,
+                                                             "vxlan",
+                                                             params=[vxlan_vni])
+            [(_, phy5_number), (_, phy6_number)] = \
+                     self._vswitch.add_veth_pair_port(self.bridge_mod1, self.bridge_mod2)
+
+            # Set up flows for the switches
+            self._vswitch.del_flow(self.bridge_phy1)
+            self._vswitch.del_flow(self.bridge_phy2)
+            self._vswitch.del_flow(self.bridge_mod1)
+            self._vswitch.del_flow(self.bridge_mod2)
+            flow = add_ports_to_flow(_FLOW_TEMPLATE, phy1_number,
+                                     phy3_number)
+            self._vswitch.add_flow(self.bridge_phy1, flow)
+            flow = add_ports_to_flow(_FLOW_TEMPLATE, phy3_number,
+                                     phy1_number)
+            self._vswitch.add_flow(self.bridge_phy1, flow)
+
+            flow = add_ports_to_flow(_FLOW_TEMPLATE, phy2_number,
+                                     phy4_number)
+            self._vswitch.add_flow(self.bridge_phy2, flow)
+            flow = add_ports_to_flow(_FLOW_TEMPLATE, phy4_number,
+                                     phy2_number)
+            self._vswitch.add_flow(self.bridge_phy2, flow)
+            flow = add_ports_to_flow(_FLOW_TEMPLATE, phy5_number,
+                                     'LOCAL')
+            self._vswitch.add_flow(self.bridge_mod1, flow)
+            mod_flow_template = _FLOW_TEMPLATE.copy()
+            mod_flow_template.update({'ip':'',
+                                      'actions':
+                                      ['mod_dl_src:' + str(vxlan_rem_mac2),
+                                       'mod_dl_dst:' + self.br_mod_mac2,
+                                       'mod_nw_src:' + vxlan_rem_ip2,
+                                       'mod_nw_dst:' + vxlan_local_ip2
+                                      ]
+                                     })
+            flow = add_ports_to_flow(mod_flow_template, 'LOCAL', phy5_number)
+            self._vswitch.add_flow(self.bridge_mod1, flow)
+            flow = add_ports_to_flow(_FLOW_TEMPLATE, phy6_number,
+                                     'LOCAL')
+            self._vswitch.add_flow(self.bridge_mod2, flow)
+            mod_flow_template = _FLOW_TEMPLATE.copy()
+            mod_flow_template.update({'ip':'',
+                                      'actions':
+                                      ['mod_dl_src:' + str(vxlan_rem_mac1),
+                                       'mod_dl_dst:' + self.br_mod_mac1,
+                                       'mod_nw_src:' + vxlan_rem_ip1,
+                                       'mod_nw_dst:' + vxlan_local_ip1]
+                                     })
+            flow = add_ports_to_flow(mod_flow_template, 'LOCAL', phy6_number)
+            self._vswitch.add_flow(self.bridge_mod2, flow)
+
+        except:
+            self._vswitch.stop()
+            raise
+
+    def stop(self):
+        """Tears down the switch created in setup().
+        """
+        self._logger.debug('Stop using ' + str(self._vswitch_class))
+        self._vswitch.stop()
+
+    def __enter__(self):
+        self.setup()
+
+    def __exit__(self, type_, value, traceback):
+        self.stop()
+
+    def get_vswitch(self):
+        """See IVswitchController for description
+        """
+        return self._vswitch
+
+    def get_ports_info(self):
+        """See IVswitchController for description
+        """
+        self._logger.debug('get_ports_info using ' + str(self._vswitch_class))
+        ports = self._vswitch.get_ports(self.bridge_phy1) +\
+                self._vswitch.get_ports(self.bridge_mod1) +\
+                self._vswitch.get_ports(self.bridge_phy2) +\
+                self._vswitch.get_ports(self.bridge_mod2)
+        return ports
+
+    def dump_vswitch_flows(self):
+        """See IVswitchController for description
+        """
+        self._logger.debug('dump_flows using ' + str(self._vswitch_class))
+        self._vswitch.dump_flows(self.bridge_phy1)
+        self._vswitch.dump_flows(self.bridge_mod1)
+        self._vswitch.dump_flows(self.bridge_phy2)
+        self._vswitch.dump_flows(self.bridge_mod2)
index c788653..51c2f24 100755 (executable)
@@ -831,3 +831,52 @@ To run GENEVE decapsulation tests:
     ./vsperf --conf-file user_settings.py --integration
              --test-params 'tunnel_type=geneve' overlay_p2p_decap_cont
 
+
+Executing Tunnel encapsulation+decapsulation tests
+--------------------------------------------------
+
+The OVS DPDK encapsulation_decapsulation tests requires IPs, MAC addresses,
+bridge names and WHITELIST_NICS for DPDK.
+
+The test cases can test the tunneling encap and decap without using any ingress
+overlay traffic as compared to above test cases. To achieve this the OVS is
+configured to perform encap and decap in a series on the same traffic stream as
+given below.
+
+TRAFFIC-IN --> [ENCAP] --> [MOD-PKT] --> [DECAP] --> TRAFFIC-OUT
+
+
+Default values are already provided. To customize for your environment, override
+the following variables in you user_settings.py file:
+
+  .. code-block:: python
+
+    # Variables defined in conf/integration/02_vswitch.conf
+
+    # Bridge names
+    TUNNEL_EXTERNAL_BRIDGE1 = 'br-phy1'
+    TUNNEL_EXTERNAL_BRIDGE2 = 'br-phy2'
+    TUNNEL_MODIFY_BRIDGE1 = 'br-mod1'
+    TUNNEL_MODIFY_BRIDGE2 = 'br-mod2'
+
+    # IP of br-mod1
+    TUNNEL_MODIFY_BRIDGE_IP1 = '10.0.0.1/24'
+
+    # Mac of br-mod1
+    TUNNEL_MODIFY_BRIDGE_MAC1 = '00:00:10:00:00:01'
+
+    # IP of br-mod2
+    TUNNEL_MODIFY_BRIDGE_IP2 = '20.0.0.1/24'
+
+    #Mac of br-mod2
+    TUNNEL_MODIFY_BRIDGE_MAC2 = '00:00:20:00:00:01'
+
+    # vxlan|gre|geneve, Only VXLAN is supported for now.
+    TUNNEL_TYPE = 'vxlan'
+
+To run VXLAN encapsulation+decapsulation tests:
+
+  .. code-block:: console
+
+    ./vsperf --conf-file user_settings.py --integration
+             overlay_p2p_mod_tput
index 243619b..d2814b6 100644 (file)
@@ -122,6 +122,38 @@ class IVSwitchOvs(IVSwitch, tasks.Process):
         """
         raise NotImplementedError
 
+    def add_veth_pair_port(self, switch_name=None, remote_switch_name=None,
+                      local_opts=None, remote_opts=None):
+        """Creates veth-pair port between 'switch_name' and 'remote_switch_name'
+
+        """
+        if switch_name is None or remote_switch_name is None:
+            return
+
+        bridge = self._bridges[switch_name]
+        remote_bridge = self._bridges[remote_switch_name]
+        pcount = str(self._get_port_count('type=patch'))
+        # TODO ::: What if interface name longer than allowed width??
+        local_port_name = switch_name + '-' + remote_switch_name + '-' + pcount
+        remote_port_name = remote_switch_name + '-' + switch_name + '-' + pcount
+        local_params = ['--', 'set', 'Interface', local_port_name,
+                        'type=patch',
+                        'options:peer=' + remote_port_name]
+        remote_params = ['--', 'set', 'Interface', remote_port_name,
+                        'type=patch',
+                        'options:peer=' + local_port_name]
+
+        if local_opts is not None:
+            local_params = local_params + local_opts
+
+        if remote_opts is not None:
+            remote_params = remote_params + remote_opts
+
+        local_of_port = bridge.add_port(local_port_name, local_params)
+        remote_of_port = remote_bridge.add_port(remote_port_name, remote_params)
+        return [(local_port_name, local_of_port),
+                (remote_port_name, remote_of_port)]
+
     def add_tunnel_port(self, switch_name, remote_ip, tunnel_type='vxlan', params=None):
         """Creates tunneling port
         """