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