From ffc55a6bbbfafc36a2e28e2e28e737966fb9bb31 Mon Sep 17 00:00:00 2001 From: Sugesh Chandran Date: Thu, 7 Jul 2016 10:24:36 +0100 Subject: integration: Support for VxLAN TC without using overlay traffic gen. 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 --- conf/integration/01_testcases.conf | 11 ++ conf/integration/02_vswitch.conf | 12 ++ core/component_factory.py | 3 + core/vswitch_controller_ptunp.py | 238 +++++++++++++++++++++++++++++++++++++ docs/userguide/integration.rst | 49 ++++++++ vswitches/ovs.py | 32 +++++ 6 files changed, 345 insertions(+) create mode 100644 core/vswitch_controller_ptunp.py diff --git a/conf/integration/01_testcases.conf b/conf/integration/01_testcases.conf index e050e358..46312637 100644 --- a/conf/integration/01_testcases.conf +++ b/conf/integration/01_testcases.conf @@ -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", diff --git a/conf/integration/02_vswitch.conf b/conf/integration/02_vswitch.conf index 6bdf79d3..68eaf73e 100644 --- a/conf/integration/02_vswitch.conf +++ b/conf/integration/02_vswitch.conf @@ -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' diff --git a/core/component_factory.py b/core/component_factory.py index a91872e2..258b7232 100644 --- a/core/component_factory.py +++ b/core/component_factory.py @@ -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 index 00000000..27d26789 --- /dev/null +++ b/core/vswitch_controller_ptunp.py @@ -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) diff --git a/docs/userguide/integration.rst b/docs/userguide/integration.rst index c7886536..51c2f241 100755 --- a/docs/userguide/integration.rst +++ b/docs/userguide/integration.rst @@ -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 diff --git a/vswitches/ovs.py b/vswitches/ovs.py index 243619b6..d2814b6a 100644 --- a/vswitches/ovs.py +++ b/vswitches/ovs.py @@ -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 """ -- cgit 1.2.3-korg