From a98884efd966fdd69ba88585b7a154ba87d1d061 Mon Sep 17 00:00:00 2001 From: Kyle Mestery Date: Fri, 17 May 2013 15:04:19 -0500 Subject: [PATCH] Add support for VXLAN to the Open vSwitch plugin. 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 --- .../openvswitch/ovs_quantum_plugin.ini | 32 ++++-- quantum/agent/linux/ovs_lib.py | 35 ++++++- .../openvswitch/agent/ovs_quantum_agent.py | 98 +++++++++++++++---- quantum/plugins/openvswitch/common/config.py | 8 +- .../plugins/openvswitch/common/constants.py | 8 ++ .../plugins/openvswitch/ovs_quantum_plugin.py | 32 +++--- .../openvswitch/test_ovs_quantum_agent.py | 42 ++++++++ .../tests/unit/openvswitch/test_ovs_tunnel.py | 44 ++++++--- 8 files changed, 239 insertions(+), 60 deletions(-) diff --git a/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini b/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini index 4813a0a58dd..fafa2a63e3a 100644 --- a/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini +++ b/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini @@ -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 # [::] 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 : 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 diff --git a/quantum/agent/linux/ovs_lib.py b/quantum/agent/linux/ovs_lib.py index 5e8cb94a0b8..b13bbc9e9a5 100644 --- a/quantum/agent/linux/ovs_lib.py +++ b/quantum/agent/linux/ovs_lib.py @@ -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.")) diff --git a/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py b/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py index 19d06fe09e7..05750e44576 100644 --- a/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py +++ b/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py @@ -19,7 +19,10 @@ # @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 diff --git a/quantum/plugins/openvswitch/common/config.py b/quantum/plugins/openvswitch/common/config.py index 4886974ddf7..3e45320ed44 100644 --- a/quantum/plugins/openvswitch/common/config.py +++ b/quantum/plugins/openvswitch/common/config.py @@ -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 :")), 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 :: " @@ -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.")), ] diff --git a/quantum/plugins/openvswitch/common/constants.py b/quantum/plugins/openvswitch/common/constants.py index 6d837fe574b..0e6425f09f7 100644 --- a/quantum/plugins/openvswitch/common/constants.py +++ b/quantum/plugins/openvswitch/common/constants.py @@ -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] diff --git a/quantum/plugins/openvswitch/ovs_quantum_plugin.py b/quantum/plugins/openvswitch/ovs_quantum_plugin.py index 417fce61f7f..1f45b0a245e 100644 --- a/quantum/plugins/openvswitch/ovs_quantum_plugin.py +++ b/quantum/plugins/openvswitch/ovs_quantum_plugin.py @@ -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, diff --git a/quantum/tests/unit/openvswitch/test_ovs_quantum_agent.py b/quantum/tests/unit/openvswitch/test_ovs_quantum_agent.py index 73bb935ee02..938794476ed 100644 --- a/quantum/tests/unit/openvswitch/test_ovs_quantum_agent.py +++ b/quantum/tests/unit/openvswitch/test_ovs_quantum_agent.py @@ -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) diff --git a/quantum/tests/unit/openvswitch/test_ovs_tunnel.py b/quantum/tests/unit/openvswitch/test_ovs_tunnel.py index 472005328d5..beade2ba0a0 100644 --- a/quantum/tests/unit/openvswitch/test_ovs_tunnel.py +++ b/quantum/tests/unit/openvswitch/test_ovs_tunnel.py @@ -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