]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add support for VXLAN to the Open vSwitch plugin.
authorKyle Mestery <kmestery@cisco.com>
Fri, 17 May 2013 20:04:19 +0000 (15:04 -0500)
committerKyle Mestery <kmestery@cisco.com>
Mon, 10 Jun 2013 12:00:20 +0000 (12:00 +0000)
This commit adds support for utilizing the VXLAN tunneling protocol in versions
of Open vSwitch >= 1.10. This is configurable and will default to GRE if not
configured. As part of this commit, it is possible to configure the UDP port
VXLAN will utilize as well. VXLAN and GRE cannot be configured at the same
time with this patch. 2 new configuration file options are added to the AGENT
section of the config to support this: 'tunnel_type' and 'vxlan_udp_port'.
In addition, the agent no longer makes use of enable_tunneling, as this can
be determined if tunnel_type is set.

Note: The VXLAN functionality utilized here is what is implemented in Open
vSwitch itself, and is different than the VXLAN functionality in the
upstream Linux kernel. The code validates both the userspace and kernel
pieces of OVS to verify if VXLAN functionality can be supported on the
running system

Implements blueprint ovs-vxlan-lisp-tunnel

Change-Id: I45d49d5d6463e574922c7f50d6499c6bdb6c862c

etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini
quantum/agent/linux/ovs_lib.py
quantum/plugins/openvswitch/agent/ovs_quantum_agent.py
quantum/plugins/openvswitch/common/config.py
quantum/plugins/openvswitch/common/constants.py
quantum/plugins/openvswitch/ovs_quantum_plugin.py
quantum/tests/unit/openvswitch/test_ovs_quantum_agent.py
quantum/tests/unit/openvswitch/test_ovs_tunnel.py

index 4813a0a58dd66f985bed70a5dec08a1796666819..fafa2a63e3a408c7733f0f08efc03043ef227aab 100644 (file)
@@ -32,12 +32,13 @@ reconnect_interval = 2
 # default value 'local' is useful only for single-box testing and
 # provides no connectivity between hosts. You MUST either change this
 # to 'vlan' and configure network_vlan_ranges below or change this to
-# 'gre' and configure tunnel_id_ranges below in order for tenant
-# networks to provide connectivity between hosts. Set to 'none' to
-# disable creation of tenant networks.
+# 'gre' or 'vxlan' and configure tunnel_id_ranges below in order for
+# tenant networks to provide connectivity between hosts. Set to 'none'
+# to disable creation of tenant networks.
 #
 # tenant_network_type = local
 # Example: tenant_network_type = gre
+# Example: tenant_network_type = vxlan
 
 # (ListOpt) Comma-separated list of
 # <physical_network>[:<vlan_min>:<vlan_max>] tuples enumerating ranges
@@ -45,20 +46,20 @@ reconnect_interval = 2
 # allocation. All physical networks listed are available for flat and
 # VLAN provider network creation. Specified ranges of VLAN IDs are
 # available for tenant network allocation if tenant_network_type is
-# 'vlan'. If empty, only gre and local networks may be created.
+# 'vlan'. If empty, only gre, vxlan and local networks may be created.
 #
 # network_vlan_ranges =
 # Example: network_vlan_ranges = physnet1:1000:2999
 
 # (BoolOpt) Set to True in the server and the agents to enable support
-# for GRE networks. Requires kernel support for OVS patch ports and
-# GRE tunneling.
+# for GRE or VXLAN networks. Requires kernel support for OVS patch ports and
+# GRE or VXLAN tunneling.
 #
 # enable_tunneling = False
 
 # (ListOpt) Comma-separated list of <tun_min>:<tun_max> tuples
-# enumerating ranges of GRE tunnel IDs that are available for tenant
-# network allocation if tenant_network_type is 'gre'.
+# enumerating ranges of GRE or VXLAN tunnel IDs that are available for
+# tenant network allocation if tenant_network_type is 'gre' or 'vxlan'.
 #
 # tunnel_id_ranges =
 # Example: tunnel_id_ranges = 1:1000
@@ -103,6 +104,21 @@ reconnect_interval = 2
 # Agent's polling interval in seconds
 # polling_interval = 2
 
+# (StrOpt) The type of tenant network tunnels to utilize when tunneling
+# is enabled. This can be set to either 'gre' or 'vxlan' currently. If
+# this is unset, it will default to 'None'.
+#
+# tunnel_type =
+# Example: tunnel_type = gre
+# Example: tunnel_type = vxlan
+
+# (IntOpt) The port number to utilize if tunnel_type is 'vxlan'. By default,
+# this will make use of the Open vSwitch default value of '4789' if not
+# specified.
+#
+# vxlan_udp_port =
+# Example: vxlan_udp_port = 8472
+
 [SECURITYGROUP]
 # Firewall driver for realizing quantum security group function.
 # firewall_driver = quantum.agent.firewall.NoopFirewallDriver
index 5e8cb94a0b86269778e403aebd87819442fd93dc..b13bbc9e9a5f87bbbaf130cd92254308cf4c81c6 100644 (file)
@@ -23,6 +23,7 @@ import re
 from quantum.agent.linux import ip_lib
 from quantum.agent.linux import utils
 from quantum.openstack.common import log as logging
+from quantum.plugins.openvswitch.common import constants
 
 LOG = logging.getLogger(__name__)
 
@@ -163,9 +164,17 @@ class OVSBridge:
         flow_str = ",".join(flow_expr_arr)
         self.run_ofctl("del-flows", [flow_str])
 
-    def add_tunnel_port(self, port_name, remote_ip):
+    def add_tunnel_port(self, port_name, remote_ip,
+                        tunnel_type=constants.TYPE_GRE,
+                        vxlan_udp_port=constants.VXLAN_UDP_PORT):
         self.run_vsctl(["add-port", self.br_name, port_name])
-        self.set_db_attribute("Interface", port_name, "type", "gre")
+        self.set_db_attribute("Interface", port_name, "type", tunnel_type)
+        if tunnel_type == constants.TYPE_VXLAN:
+            # Only set the VXLAN UDP port if it's not the default
+            if vxlan_udp_port != constants.VXLAN_UDP_PORT:
+                self.set_db_attribute("Interface", port_name,
+                                      "options:dst_port",
+                                      vxlan_udp_port)
         self.set_db_attribute("Interface", port_name, "options:remote_ip",
                               remote_ip)
         self.set_db_attribute("Interface", port_name, "options:in_key", "flow")
@@ -310,3 +319,25 @@ def get_bridges(root_helper):
     except Exception as e:
         LOG.exception(_("Unable to retrieve bridges. Exception: %s"), e)
         return []
+
+
+def get_installed_ovs_usr_version(root_helper):
+    args = ["ovs-vsctl", "--version"]
+    try:
+        cmd = utils.execute(args, root_helper=root_helper)
+        ver = re.findall("\d+\.\d+", cmd)[0]
+        return ver
+    except Exception:
+        LOG.exception(_("Unable to retrieve OVS userspace version."))
+
+
+def get_installed_ovs_klm_version():
+    args = ["modinfo", "openvswitch"]
+    try:
+        cmd = utils.execute(args)
+        for line in cmd.split('\n'):
+            if 'version: ' in line and not 'srcversion' in line:
+                ver = re.findall("\d+\.\d+", line)
+                return ver[0]
+    except Exception:
+        LOG.exception(_("Unable to retrieve OVS kernel module version."))
index 19d06fe09e7061c63afa419f12661db02bc6dbff..05750e44576c78037e2dcb284d620a7712d7433b 100644 (file)
 # @author: Dan Wendlandt, Nicira Networks, Inc.
 # @author: Dave Lapsley, Nicira Networks, Inc.
 # @author: Aaron Rosen, Nicira Networks, Inc.
+# @author: Seetharama Ayyadevara, Freescale Semiconductor, Inc.
+# @author: Kyle Mestery, Cisco Systems, Inc.
 
+import distutils.version as dist_version
 import sys
 import time
 
@@ -146,7 +149,7 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
 
     def __init__(self, integ_br, tun_br, local_ip,
                  bridge_mappings, root_helper,
-                 polling_interval, enable_tunneling):
+                 polling_interval, tunnel_type=constants.TYPE_NONE):
         '''Constructor.
 
         :param integ_br: name of the integration bridge.
@@ -155,7 +158,8 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
         :param bridge_mappings: mappings from physical network name to bridge.
         :param root_helper: utility to use when running shell cmds.
         :param polling_interval: interval (secs) to poll DB.
-        :param enable_tunneling: if True enable GRE networks.
+        :param tunnel_type: Either gre or vxlan. If set, will automatically
+               set enable_tunneling to True.
         '''
         self.root_helper = root_helper
         self.available_local_vlans = set(xrange(q_const.MIN_VLAN_TAG,
@@ -166,9 +170,15 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
 
         self.polling_interval = polling_interval
 
-        self.enable_tunneling = enable_tunneling
+        if tunnel_type in constants.TUNNEL_NETWORK_TYPES:
+            self.enable_tunneling = True
+        else:
+            self.enable_tunneling = False
         self.local_ip = local_ip
         self.tunnel_count = 0
+        self.tunnel_type = tunnel_type
+        self.vxlan_udp_port = cfg.CONF.AGENT.vxlan_udp_port
+        self._check_ovs_version()
         if self.enable_tunneling:
             self.setup_tunnel_br(tun_br)
         self.agent_state = {
@@ -177,6 +187,7 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
             'topic': q_const.L2_AGENT_TOPIC,
             'configurations': bridge_mappings,
             'agent_type': q_const.AGENT_TYPE_OVS,
+            'tunnel_type': self.tunnel_type,
             'start_flag': True}
         self.setup_rpc()
 
@@ -185,6 +196,11 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
                                               self.plugin_rpc,
                                               root_helper)
 
+    def _check_ovs_version(self):
+        if self.enable_tunneling and self.tunnel_type == constants.TYPE_VXLAN:
+            check_ovs_version(constants.MINIMUM_OVS_VXLAN_VERSION,
+                              self.root_helper)
+
     def _report_state(self):
         try:
             # How many devices are likely used by a VM
@@ -274,8 +290,9 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
         tunnel_id = kwargs.get('tunnel_id')
         if tunnel_ip == self.local_ip:
             return
-        tun_name = 'gre-%s' % tunnel_id
-        self.tun_br.add_tunnel_port(tun_name, tunnel_ip)
+        tun_name = '%s-%s' % (self.tunnel_type, tunnel_id)
+        self.tun_br.add_tunnel_port(tun_name, tunnel_ip, self.tunnel_type,
+                                    self.vxlan_udp_port)
 
     def create_rpc_dispatcher(self):
         '''Get the rpc dispatcher for this manager.
@@ -290,7 +307,8 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
         '''Provisions a local VLAN.
 
         :param net_uuid: the uuid of the network associated with this vlan.
-        :param network_type: the network type ('gre', 'vlan', 'flat', 'local')
+        :param network_type: the network type ('gre', 'vxlan', 'vlan', 'flat',
+                                               'local')
         :param physical_network: the physical network for 'vlan' or 'flat'
         :param segmentation_id: the VID for 'vlan' or tunnel ID for 'tunnel'
         '''
@@ -306,7 +324,7 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
                                                          physical_network,
                                                          segmentation_id)
 
-        if network_type == constants.TYPE_GRE:
+        if network_type in constants.TUNNEL_NETWORK_TYPES:
             if self.enable_tunneling:
                 # outbound
                 self.tun_br.add_flow(priority=4, in_port=self.patch_int_ofport,
@@ -321,8 +339,10 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
                     actions="mod_vlan_vid:%s,output:%s" %
                     (lvid, self.patch_int_ofport))
             else:
-                LOG.error(_("Cannot provision GRE network for net-id=%s "
-                          "- tunneling disabled"), net_uuid)
+                LOG.error(_("Cannot provision %(network_type)s network for "
+                          "net-id=%(net_uuid)s - tunneling disabled"),
+                          {'network_type': network_type,
+                           'net_uuid': net_uuid})
         elif network_type == constants.TYPE_FLAT:
             if physical_network in self.phys_brs:
                 # outbound
@@ -383,7 +403,7 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
                  {'vlan_id': lvm.vlan,
                   'net_uuid': net_uuid})
 
-        if lvm.network_type == constants.TYPE_GRE:
+        if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
             if self.enable_tunneling:
                 self.tun_br.delete_flows(tun_id=lvm.segmentation_id)
                 self.tun_br.delete_flows(dl_vlan=lvm.vlan)
@@ -438,7 +458,7 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
         lvm = self.local_vlan_map[net_uuid]
         lvm.vif_ports[port.vif_id] = port
 
-        if network_type == constants.TYPE_GRE:
+        if network_type in constants.TUNNEL_NETWORK_TYPES:
             if self.enable_tunneling:
                 # inbound unicast
                 self.tun_br.add_flow(priority=3, tun_id=segmentation_id,
@@ -471,7 +491,8 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
 
         vif_port = lvm.vif_ports.pop(vif_id, None)
         if vif_port:
-            if self.enable_tunneling and lvm.network_type == 'gre':
+            if self.enable_tunneling and lvm.network_type in (
+                    constants.TUNNEL_NETWORK_TYPES):
                 # remove inbound unicast flow
                 self.tun_br.delete_flows(tun_id=lvm.segmentation_id,
                                          dl_dst=vif_port.vif_mac)
@@ -674,8 +695,10 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
             tunnels = details['tunnels']
             for tunnel in tunnels:
                 if self.local_ip != tunnel['ip_address']:
-                    tun_name = 'gre-%s' % tunnel['id']
-                    self.tun_br.add_tunnel_port(tun_name, tunnel['ip_address'])
+                    tun_name = '%s-%s' % (self.tunnel_type, tunnel['id'])
+                    self.tun_br.add_tunnel_port(tun_name, tunnel['ip_address'],
+                                                self.tunnel_type,
+                                                self.vxlan_udp_port)
         except Exception as e:
             LOG.debug(_("Unable to sync tunnel IP %(local_ip)s: %(e)s"),
                       {'local_ip': self.local_ip, 'e': e})
@@ -728,6 +751,44 @@ class OVSQuantumAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
         self.rpc_loop()
 
 
+def check_ovs_version(min_required_version, root_helper):
+    LOG.debug(_("Checking OVS version for VXLAN support"))
+    installed_klm_version = ovs_lib.get_installed_ovs_klm_version()
+    installed_usr_version = ovs_lib.get_installed_ovs_usr_version(root_helper)
+    # First check the userspace version
+    if installed_usr_version:
+        if dist_version.StrictVersion(
+                installed_usr_version) < dist_version.StrictVersion(
+                min_required_version):
+            LOG.error(_('Failed userspace version check for Open '
+                        'vSwitch with VXLAN support. To use '
+                        'VXLAN tunnels with OVS, please ensure '
+                        'the OVS version is %s '
+                        'or newer!'), min_required_version)
+            sys.exit(1)
+        # Now check the kernel version
+        if installed_klm_version:
+            if dist_version.StrictVersion(
+                    installed_klm_version) < dist_version.StrictVersion(
+                    min_required_version):
+                LOG.error(_('Failed kernel version check for Open '
+                            'vSwitch with VXLAN support. To use '
+                            'VXLAN tunnels with OVS, please ensure '
+                            'the OVS version is %s or newer!'),
+                          min_required_version)
+                sys.exti(1)
+            else:
+                LOG.warning(_('Cannot determine kernel Open vSwitch version, '
+                              'please ensure your Open vSwitch kernel module '
+                              'is at least version %s to support VXLAN '
+                              'tunnels.'), min_required_version)
+    else:
+        LOG.warning(_('Unable to determine Open vSwitch version. Please '
+                      'ensure that its version is %s or newer to use VXLAN '
+                      'tunnels with OVS.'), min_required_version)
+        sys.exit(1)
+
+
 def create_agent_config_map(config):
     """Create a map of agent config parameters.
 
@@ -746,12 +807,13 @@ def create_agent_config_map(config):
         bridge_mappings=bridge_mappings,
         root_helper=config.AGENT.root_helper,
         polling_interval=config.AGENT.polling_interval,
-        enable_tunneling=config.OVS.enable_tunneling,
+        tunnel_type=config.AGENT.tunnel_type,
     )
 
-    if kwargs['enable_tunneling'] and not kwargs['local_ip']:
-        msg = _('Tunnelling cannot be enabled without a valid local_ip.')
-        raise ValueError(msg)
+    if kwargs['tunnel_type'] in constants.TUNNEL_NETWORK_TYPES:
+        if not kwargs['local_ip']:
+            msg = _('Tunneling cannot be enabled without a valid local_ip.')
+            raise ValueError(msg)
 
     return kwargs
 
index 4886974ddf76062bf716e1731707fba5bf7354ba..3e45320ed4429d6012eaf8682ab790fae06ea640 100644 (file)
@@ -17,6 +17,7 @@
 from oslo.config import cfg
 
 from quantum.agent.common import config
+from quantum.plugins.openvswitch.common import constants
 from quantum import scheduler
 
 
@@ -44,7 +45,7 @@ ovs_opts = [
                 help=_("List of <physical_network>:<bridge>")),
     cfg.StrOpt('tenant_network_type', default='local',
                help=_("Network type for tenant networks "
-                      "(local, vlan, gre, or none)")),
+                      "(local, vlan, gre, vxlan, or none)")),
     cfg.ListOpt('network_vlan_ranges',
                 default=DEFAULT_VLAN_RANGES,
                 help=_("List of <physical_network>:<vlan_min>:<vlan_max> "
@@ -58,6 +59,11 @@ agent_opts = [
     cfg.IntOpt('polling_interval', default=2,
                help=_("The number of seconds the agent will wait between "
                       "polling for local device changes.")),
+    cfg.StrOpt('tunnel_type', default=None,
+               help=_("Network type for agent tunnel networks "
+                      "(gre or vxlan)")),
+    cfg.IntOpt('vxlan_udp_port', default=constants.VXLAN_UDP_PORT,
+               help=_("The UDP port to use for VXLAN tunnels.")),
 ]
 
 
index 6d837fe574b22eeac328748247527efa9c9770a6..0e6425f09f78d1551484bba1711e28723a88328a 100644 (file)
@@ -24,9 +24,17 @@ TYPE_FLAT = 'flat'
 TYPE_VLAN = 'vlan'
 TYPE_GRE = 'gre'
 TYPE_LOCAL = 'local'
+TYPE_VXLAN = 'vxlan'
 TYPE_NONE = 'none'
+VXLAN_UDP_PORT = 4789
 
 # Name prefixes for veth device pair linking the integration bridge
 # with the physical bridge for a physical network
 VETH_INTEGRATION_PREFIX = 'int-'
 VETH_PHYSICAL_PREFIX = 'phy-'
+
+# The minimum version of OVS which supports VXLAN tunneling
+MINIMUM_OVS_VXLAN_VERSION = "1.10"
+
+# The different types of tunnels
+TUNNEL_NETWORK_TYPES = [TYPE_GRE, TYPE_VXLAN]
index 417fce61f7f3d06bd351a76a010fddbc6cb7b048..1f45b0a245e5e318b1f6bd096280d238d159bd27 100644 (file)
@@ -19,6 +19,7 @@
 # @author: Dave Lapsley, Nicira Networks, Inc.
 # @author: Aaron Rosen, Nicira Networks, Inc.
 # @author: Bob Kukura, Red Hat, Inc.
+# @author: Seetharama Ayyadevara, Freescale Semiconductor, Inc.
 
 import sys
 
@@ -222,7 +223,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
 
     """Implement the Quantum abstractions using Open vSwitch.
 
-    Depending on whether tunneling is enabled, either a GRE tunnel or
+    Depending on whether tunneling is enabled, either a GRE, VXLAN tunnel or
     a new VLAN is created for each network. An agent is relied upon to
     perform the actual OVS configuration on each host.
 
@@ -269,9 +270,10 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         if self.tenant_network_type not in [constants.TYPE_LOCAL,
                                             constants.TYPE_VLAN,
                                             constants.TYPE_GRE,
+                                            constants.TYPE_VXLAN,
                                             constants.TYPE_NONE]:
             LOG.error(_("Invalid tenant_network_type: %s. "
-                      "Agent terminated!"),
+                      "Server terminated!"),
                       self.tenant_network_type)
             sys.exit(1)
         self.enable_tunneling = cfg.CONF.OVS.enable_tunneling
@@ -279,9 +281,9 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         if self.enable_tunneling:
             self._parse_tunnel_id_ranges()
             ovs_db_v2.sync_tunnel_allocations(self.tunnel_id_ranges)
-        elif self.tenant_network_type == constants.TYPE_GRE:
-            LOG.error(_("Tunneling disabled but tenant_network_type is 'gre'. "
-                      "Agent terminated!"))
+        elif self.tenant_network_type in constants.TUNNEL_NETWORK_TYPES:
+            LOG.error(_("Tunneling disabled but tenant_network_type is '%s'. "
+                      "Server terminated!"), self.tenant_network_type)
             sys.exit(1)
         self.setup_rpc()
         self.network_scheduler = importutils.import_object(
@@ -308,7 +310,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             self.network_vlan_ranges = plugin_utils.parse_network_vlan_ranges(
                 cfg.CONF.OVS.network_vlan_ranges)
         except Exception as ex:
-            LOG.error(_("%s. Agent terminated!"), ex)
+            LOG.error(_("%s. Server terminated!"), ex)
             sys.exit(1)
         LOG.info(_("Network VLAN ranges: %s"), self.network_vlan_ranges)
 
@@ -320,7 +322,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 self.tunnel_id_ranges.append((int(tun_min), int(tun_max)))
             except ValueError as ex:
                 LOG.error(_("Invalid tunnel ID range: "
-                            "'%(range)s' - %(e)s. Agent terminated!"),
+                            "'%(range)s' - %(e)s. Server terminated!"),
                           {'range': entry, 'e': ex})
                 sys.exit(1)
         LOG.info(_("Tunnel ID ranges: %s"), self.tunnel_id_ranges)
@@ -329,7 +331,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         binding = ovs_db_v2.get_network_binding(context.session,
                                                 network['id'])
         network[provider.NETWORK_TYPE] = binding.network_type
-        if binding.network_type == constants.TYPE_GRE:
+        if binding.network_type in constants.TUNNEL_NETWORK_TYPES:
             network[provider.PHYSICAL_NETWORK] = None
             network[provider.SEGMENTATION_ID] = binding.segmentation_id
         elif binding.network_type == constants.TYPE_FLAT:
@@ -374,13 +376,13 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                        {'min_id': q_const.MIN_VLAN_TAG,
                         'max_id': q_const.MAX_VLAN_TAG})
                 raise q_exc.InvalidInput(error_message=msg)
-        elif network_type == constants.TYPE_GRE:
+        elif network_type in constants.TUNNEL_NETWORK_TYPES:
             if not self.enable_tunneling:
-                msg = _("GRE networks are not enabled")
+                msg = _("%s networks are not enabled") % network_type
                 raise q_exc.InvalidInput(error_message=msg)
             if physical_network_set:
-                msg = _("provider:physical_network specified for GRE "
-                        "network")
+                msg = _("provider:physical_network specified for %s "
+                        "network") % network_type
                 raise q_exc.InvalidInput(error_message=msg)
             else:
                 physical_network = None
@@ -454,7 +456,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 elif network_type == constants.TYPE_VLAN:
                     (physical_network,
                      segmentation_id) = ovs_db_v2.reserve_vlan(session)
-                elif network_type == constants.TYPE_GRE:
+                elif network_type in constants.TUNNEL_NETWORK_TYPES:
                     segmentation_id = ovs_db_v2.reserve_tunnel(session)
                 # no reservation needed for TYPE_LOCAL
             else:
@@ -462,7 +464,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 if network_type in [constants.TYPE_VLAN, constants.TYPE_FLAT]:
                     ovs_db_v2.reserve_specific_vlan(session, physical_network,
                                                     segmentation_id)
-                elif network_type == constants.TYPE_GRE:
+                elif network_type in constants.TUNNEL_NETWORK_TYPES:
                     ovs_db_v2.reserve_specific_tunnel(session, segmentation_id)
                 # no reservation needed for TYPE_LOCAL
             net = super(OVSQuantumPluginV2, self).create_network(context,
@@ -494,7 +496,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
         with session.begin(subtransactions=True):
             binding = ovs_db_v2.get_network_binding(session, id)
             super(OVSQuantumPluginV2, self).delete_network(context, id)
-            if binding.network_type == constants.TYPE_GRE:
+            if binding.network_type in constants.TUNNEL_NETWORK_TYPES:
                 ovs_db_v2.release_tunnel(session, binding.segmentation_id,
                                          self.tunnel_id_ranges)
             elif binding.network_type in [constants.TYPE_VLAN,
index 73bb935ee0204c719620ea4c70dd8101d6f4504f..938794476eda1195029618478ce616677c376923 100644 (file)
@@ -25,6 +25,7 @@ from quantum.agent.linux import ip_lib
 from quantum.agent.linux import ovs_lib
 from quantum.openstack.common.rpc import common as rpc_common
 from quantum.plugins.openvswitch.agent import ovs_quantum_agent
+from quantum.plugins.openvswitch.common import constants
 from quantum.tests import base
 
 
@@ -41,6 +42,7 @@ class CreateAgentConfigMap(base.BaseTestCase):
         self.addCleanup(cfg.CONF.reset)
         # An ip address is required for tunneling but there is no default
         cfg.CONF.set_override('enable_tunneling', True, group='OVS')
+        cfg.CONF.set_override('tunnel_type', 'gre', group='AGENT')
         with testtools.ExpectedException(ValueError):
             ovs_quantum_agent.create_agent_config_map(cfg.CONF)
 
@@ -310,3 +312,43 @@ class TestOvsQuantumAgent(base.BaseTestCase):
             lvm.vif_ports = {"vif1": mock.Mock()}
             self.agent.port_unbound("vif3", "netuid12345")
             self.assertEqual(reclvl_fn.call_count, 2)
+
+    def _check_ovs_vxlan_version(self, installed_version, min_vers,
+                                 expecting_ok):
+        with mock.patch(
+                'quantum.agent.linux.ovs_lib.get_installed_ovs_klm_version'
+        ) as klm_cmd:
+            with mock.patch(
+                'quantum.agent.linux.ovs_lib.get_installed_ovs_usr_version'
+            ) as usr_cmd:
+                try:
+                    klm_cmd.return_value = installed_version
+                    usr_cmd.return_value = installed_version
+                    self.agent.tunnel_type = 'vxlan'
+                    ovs_quantum_agent.check_ovs_version(min_vers,
+                                                        root_helper='sudo')
+                    version_ok = True
+                except SystemExit as e:
+                    self.assertEquals(e.code, 1)
+                    version_ok = False
+            self.assertEqual(version_ok, expecting_ok)
+
+    def test_check_minimum_version(self):
+        self._check_ovs_vxlan_version('1.10',
+                                      constants.MINIMUM_OVS_VXLAN_VERSION,
+                                      expecting_ok=True)
+
+    def test_check_future_version(self):
+        self._check_ovs_vxlan_version('1.11',
+                                      constants.MINIMUM_OVS_VXLAN_VERSION,
+                                      expecting_ok=True)
+
+    def test_check_fail_version(self):
+        self._check_ovs_vxlan_version('1.9',
+                                      constants.MINIMUM_OVS_VXLAN_VERSION,
+                                      expecting_ok=False)
+
+    def test_check_fail_no_version(self):
+        self._check_ovs_vxlan_version(None,
+                                      constants.MINIMUM_OVS_VXLAN_VERSION,
+                                      expecting_ok=False)
index 472005328d56a0dea8762e9cc68c753e57127ae7..beade2ba0a050d157b0b8c4dfa94cb6458071604 100644 (file)
@@ -129,7 +129,19 @@ class TunnelTest(base.BaseTestCase):
         ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                           self.TUN_BRIDGE,
                                           '10.0.0.1', self.NET_MAPPING,
-                                          'sudo', 2, True)
+                                          'sudo', 2, 'gre')
+        self.mox.VerifyAll()
+
+    def testConstructVXLAN(self):
+        self.mox.StubOutWithMock(ovs_lib, 'get_installed_ovs_klm_version')
+        ovs_lib.get_installed_ovs_klm_version().AndReturn("1.10")
+        self.mox.StubOutWithMock(ovs_lib, 'get_installed_ovs_usr_version')
+        ovs_lib.get_installed_ovs_usr_version('sudo').AndReturn("1.10")
+        self.mox.ReplayAll()
+        ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
+                                          self.TUN_BRIDGE,
+                                          '10.0.0.1', self.NET_MAPPING,
+                                          'sudo', 2, 'vxlan')
         self.mox.VerifyAll()
 
     def testProvisionLocalVlan(self):
@@ -146,7 +158,7 @@ class TunnelTest(base.BaseTestCase):
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.available_local_vlans = set([LV_ID])
         a.provision_local_vlan(NET_UUID, constants.TYPE_GRE, None, LS_ID)
         self.mox.VerifyAll()
@@ -166,7 +178,7 @@ class TunnelTest(base.BaseTestCase):
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.available_local_vlans = set([LV_ID])
         a.phys_brs['net1'] = self.mock_map_tun_bridge
         a.phys_ofports['net1'] = self.MAP_TUN_OFPORT
@@ -179,7 +191,7 @@ class TunnelTest(base.BaseTestCase):
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.provision_local_vlan(NET_UUID, constants.TYPE_FLAT, 'net2', LS_ID)
         self.mox.VerifyAll()
 
@@ -197,7 +209,7 @@ class TunnelTest(base.BaseTestCase):
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.available_local_vlans = set([LV_ID])
         a.phys_brs['net1'] = self.mock_map_tun_bridge
         a.phys_ofports['net1'] = self.MAP_TUN_OFPORT
@@ -210,7 +222,7 @@ class TunnelTest(base.BaseTestCase):
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.provision_local_vlan(NET_UUID, constants.TYPE_VLAN, 'net2', LS_ID)
         self.mox.VerifyAll()
 
@@ -223,7 +235,7 @@ class TunnelTest(base.BaseTestCase):
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.available_local_vlans = set()
         a.local_vlan_map[NET_UUID] = LVM
         a.reclaim_local_vlan(NET_UUID, LVM)
@@ -241,7 +253,7 @@ class TunnelTest(base.BaseTestCase):
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.phys_brs['net1'] = self.mock_map_tun_bridge
         a.phys_ofports['net1'] = self.MAP_TUN_OFPORT
         a.int_ofports['net1'] = self.INT_OFPORT
@@ -263,7 +275,7 @@ class TunnelTest(base.BaseTestCase):
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.phys_brs['net1'] = self.mock_map_tun_bridge
         a.phys_ofports['net1'] = self.MAP_TUN_OFPORT
         a.int_ofports['net1'] = self.INT_OFPORT
@@ -288,7 +300,7 @@ class TunnelTest(base.BaseTestCase):
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.local_vlan_map[NET_UUID] = LVM
         a.port_bound(VIF_PORT, NET_UUID, 'gre', None, LS_ID)
         self.mox.VerifyAll()
@@ -308,7 +320,7 @@ class TunnelTest(base.BaseTestCase):
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.local_vlan_map[NET_UUID] = LVM
         a.port_bound(VIF_PORT, NET_UUID, 'gre', None, LS_ID)
         a.available_local_vlans = set([LV_ID])
@@ -327,19 +339,19 @@ class TunnelTest(base.BaseTestCase):
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.available_local_vlans = set([LV_ID])
         a.local_vlan_map[NET_UUID] = LVM
         a.port_dead(VIF_PORT)
         self.mox.VerifyAll()
 
     def testTunnelUpdate(self):
-        self.mock_tun_bridge.add_tunnel_port('gre-1', '10.0.10.1')
+        self.mock_tun_bridge.add_tunnel_port('gre-1', '10.0.10.1', 'gre', 4789)
         self.mox.ReplayAll()
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.tunnel_update(
             mox.MockAnything, tunnel_id='1', tunnel_ip='10.0.10.1')
         self.mox.VerifyAll()
@@ -349,7 +361,7 @@ class TunnelTest(base.BaseTestCase):
         a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
                                               self.TUN_BRIDGE,
                                               '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, True)
+                                              'sudo', 2, 'gre')
         a.tunnel_update(
             mox.MockAnything, tunnel_id='1', tunnel_ip='10.0.0.1')
         self.mox.VerifyAll()
@@ -389,7 +401,7 @@ class TunnelTest(base.BaseTestCase):
                                                     self.TUN_BRIDGE,
                                                     '10.0.0.1',
                                                     self.NET_MAPPING,
-                                                    'sudo', 2, True)
+                                                    'sudo', 2, 'gre')
 
         # Hack to test loop
         # We start method and expect it will raise after 2nd loop