From 88cd4f85047cb8844fd192f3c8d4b087b4085a1a Mon Sep 17 00:00:00 2001 From: Bob Kukura Date: Tue, 4 Sep 2012 12:30:34 -0400 Subject: [PATCH] add local network type and use by default for tenant networks Fixes bug 1045142. This patch adds 'local' as a new value for provider:network_type, supported by the openvswitch and linuxbridge plugins. Networks of this type provide connectivity through a bridge for VMs and agents local to the host, but no external connectivity. They do not require provider:physical_network or provider:segmentation_id values. These local networks are intended mainly to support single-box zero-configuration testing (including quantum gating), but may have other uses as well. For openvswitch, the new OVS.tenant_network_type configuration variable selects what type of networks are allocated as tenant (i.e. non-provider) networks. It defaults to 'local', but must be changed to 'vlan' or 'gre' for openvswitch tenant networks to have external connectivity. The default value is intended to support single-box zero-configuration testing without any need to allocate physical network resources or configure bridges, and without requiring the operating system to support OVS GRE tunneling. It can also be set to 'none' to completely disable creation of tenant networks. For linuxbridge, the new VLANS.tenant_network_type configuration variable works similarly, with a value of 'vlan' supporting tenant networks with external connectivity. With either plugin, administrators can create provider local networks by specifying "--provider:network_type local". Additionally, with openvswitch, provider GRE networks can now be created by specifying "--provider:network_type gre --provider:segmentation_id ". A corresponding devstack patch is available at https://review.openstack.org/#/c/12456/. With this patch, the openvswitch and linuxbridge plugins are by default configured to support only local networks. A set of shell variables, documented in stack.sh, can be set in localrc to configure remote connectivity, including bridges/interfaces available for provider networks. Change-Id: I2812548326141d2212d04f34d5448fb974d298e0 --- .../plugins/linuxbridge/linuxbridge_conf.ini | 32 +++++-- .../openvswitch/ovs_quantum_plugin.ini | 64 ++++++++++---- quantum/common/exceptions.py | 4 + quantum/extensions/providernet.py | 11 ++- .../agent/linuxbridge_quantum_agent.py | 53 ++++++++--- quantum/plugins/linuxbridge/common/config.py | 7 +- .../plugins/linuxbridge/common/constants.py | 12 +-- .../linuxbridge/db/l2network_models_v2.py | 2 +- .../plugins/linuxbridge/lb_quantum_plugin.py | 88 +++++++++++++------ .../openvswitch/agent/ovs_quantum_agent.py | 10 ++- quantum/plugins/openvswitch/common/config.py | 7 +- .../plugins/openvswitch/common/constants.py | 2 + quantum/plugins/openvswitch/ovs_models_v2.py | 3 +- .../plugins/openvswitch/ovs_quantum_plugin.py | 78 ++++++++++++---- .../tests/unit/linuxbridge/test_defaults.py | 15 ++-- .../unit/openvswitch/test_ovs_defaults.py | 15 +--- 16 files changed, 284 insertions(+), 119 deletions(-) diff --git a/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini b/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini index b571cc0a6..2523a658a 100644 --- a/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini +++ b/etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini @@ -1,9 +1,24 @@ [VLANS] +# (StrOpt) Type of network to allocate for tenant networks. The +# default value 'local' is useful only for single-box testing and +# provides no connectivity between hosts. You MUST change this to +# 'vlan' and configure network_vlan_ranges below in order for tenant +# networks to provide connectivity between hosts. Set to 'none' to +# disable creation of tenant networks. +# +# Default: tenant_network_type = local +# Example: tenant_network_type = vlan + # (ListOpt) Comma-separated list of -# :: tuples enumerating ranges +# [::] tuples enumerating ranges # of VLAN IDs on named physical networks that are available for -# allocation. -# network_vlan_ranges = default:1000:2999 +# 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 local networks may be created. +# +# Default: network_vlan_ranges = +# Example: network_vlan_ranges = physnet1:1000:2999 [DATABASE] # This line MUST be changed to actually run the plugin. @@ -21,10 +36,13 @@ reconnect_interval = 2 [LINUX_BRIDGE] # (ListOpt) Comma-separated list of # : tuples mapping physical -# network names to agent's node-specific physical network -# interfaces. Server uses physical network names for validation but -# ignores interfaces. -# physical_interface_mappings = default:eth1 +# network names to the agent's node-specific physical network +# interfaces to be used for flat and VLAN networks. All physical +# networks listed in network_vlan_ranges on the server should have +# mappings to appropriate interfaces on each agent. +# +# Default: physical_interface_mappings = +# Example: physical_interface_mappings = physnet1:eth1 [AGENT] # Agent's polling interval in seconds diff --git a/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini b/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini index 1b1b87087..ea3db361d 100644 --- a/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini +++ b/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini @@ -12,37 +12,63 @@ sql_connection = sqlite:// reconnect_interval = 2 [OVS] +# (StrOpt) Type of network to allocate for tenant networks. The +# 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. +# +# Default: tenant_network_type = local +# Example: tenant_network_type = gre + # (ListOpt) Comma-separated list of -# :: tuples enumerating ranges +# [::] tuples enumerating ranges # of VLAN IDs on named physical networks that are available for -# allocation. -# network_vlan_ranges = default:1000:2999 +# 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. +# +# Default: network_vlan_ranges = +# Example: network_vlan_ranges = physnet1:1000:2999 # (ListOpt) Comma-separated list of : tuples -# enumerating ranges of GRE tunnel IDs that are available for -# allocation. -# tunnel_id_ranges = +# enumerating ranges of GRE tunnel IDs that are available for tenant +# network allocation if tenant_network_type is 'gre'. +# +# Default: tunnel_id_ranges = +# Example: tunnel_id_ranges = 1:1000 # Do not change this parameter unless you have a good reason to. # This is the name of the OVS integration bridge. There is one per hypervisor. -# The integration bridge acts as a virtual "patch port". All VM VIFs are +# The integration bridge acts as a virtual "patch bay". All VM VIFs are # attached to this bridge and then "patched" according to their network # connectivity. -# integration_bridge = br-int +# +# Default: integration_bridge = br-int -# Only used if tunnel_id_ranges (above) is not empty. -# In most cases, the default value should be fine. -# tunnel_bridge = br-tun +# Only used for the agent if tunnel_id_ranges (above) is not empty for +# the server. In most cases, the default value should be fine. +# +# Default: tunnel_bridge = br-tun + +# Uncomment this line for the agent if tunnel_id_ranges (above) is not +# empty for the server. Set local-ip to be the local IP address of +# this hypervisor. +# +# Default: local_ip = 10.0.0.3 # (ListOpt) Comma-separated list of : tuples -# mapping physical network names to agent's node-specific OVS bridge -# names. Each bridge must exist, and should have physical network -# interface configured as a port. -# bridge_mappings = default:br-eth1 - -# Uncomment this line if tunnel_id_ranges (above) is not empty. -# Set local-ip to be the local IP address of this hypervisor. -# local_ip = 10.0.0.3 +# mapping physical network names to the agent's node-specific OVS +# bridge names to be used for flat and VLAN networks. Each bridge must +# exist, and should have a physical network interface configured as a +# port. All physical networks listed in network_vlan_ranges on the +# server should have mappings to appropriate bridges on each agent. +# +# Default: bridge_mappings = +# Example: bridge_mappings = physnet1:br-eth1 [AGENT] # Agent's polling interval in seconds diff --git a/quantum/common/exceptions.py b/quantum/common/exceptions.py index 0a117c3a2..bed98faff 100644 --- a/quantum/common/exceptions.py +++ b/quantum/common/exceptions.py @@ -137,6 +137,10 @@ class TunnelIdInUse(InUse): "The tunnel ID %(tunnel_id)s is in use.") +class TenantNetworksDisabled(QuantumException): + message = _("Tenant network creation is not enabled.") + + class ResourceExhausted(QuantumException): pass diff --git a/quantum/extensions/providernet.py b/quantum/extensions/providernet.py index 0b41494e0..204b7164c 100644 --- a/quantum/extensions/providernet.py +++ b/quantum/extensions/providernet.py @@ -19,11 +19,12 @@ NETWORK_TYPE = 'provider:network_type' PHYSICAL_NETWORK = 'provider:physical_network' SEGMENTATION_ID = 'provider:segmentation_id' +NETWORK_TYPE_VALUES = ['flat', 'gre', 'local', 'vlan'] + EXTENDED_ATTRIBUTES_2_0 = { 'networks': { NETWORK_TYPE: {'allow_post': True, 'allow_put': True, - 'validate': {'type:values': ['flat', - 'vlan']}, + 'validate': {'type:values': NETWORK_TYPE_VALUES}, 'default': attributes.ATTR_NOT_SPECIFIED, 'is_visible': True}, PHYSICAL_NETWORK: {'allow_post': True, 'allow_put': True, @@ -49,6 +50,8 @@ class Providernet(object): To create a provider VLAN network using the CLI with admin rights: (shell) net-create --tenant_id \ + --provider:network_type vlan \ + --provider:physical_network \ --provider:segmentation_id With admin rights, network dictionaries returned from CLI commands @@ -65,7 +68,7 @@ class Providernet(object): @classmethod def get_description(cls): - return "Expose mapping of virtual networks to VLANs and flat networks" + return "Expose mapping of virtual networks to physical networks" @classmethod def get_namespace(cls): @@ -73,7 +76,7 @@ class Providernet(object): @classmethod def get_updated(cls): - return "2012-07-23T10:00:00-00:00" + return "2012-09-07T10:00:00-00:00" def get_extended_resources(self, version): if version == "2.0": diff --git a/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py b/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py index 5b467a86e..2cb25c32d 100755 --- a/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py +++ b/quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py @@ -192,6 +192,11 @@ class LinuxBridge: self.ensure_bridge(bridge_name, physical_interface, ips, gateway) return physical_interface + def ensure_local_bridge(self, network_id): + """Create a local bridge unless it already exists.""" + bridge_name = self.get_bridge_name(network_id) + self.ensure_bridge(bridge_name) + def ensure_vlan(self, physical_interface, vlan_id): """Create a vlan unless it already exists.""" interface = self.get_subinterface_name(physical_interface, vlan_id) @@ -237,7 +242,8 @@ class LinuxBridge: src_device.addr.delete(ip_version=ip['ip_version'], cidr=ip['cidr']) - def ensure_bridge(self, bridge_name, interface, ips=None, gateway=None): + def ensure_bridge(self, bridge_name, interface=None, ips=None, + gateway=None): """ Create a bridge unless it already exists. """ @@ -259,6 +265,9 @@ class LinuxBridge: LOG.debug("Done starting bridge %s for subinterface %s" % (bridge_name, interface)) + if not interface: + return + # Update IP info if necessary self.update_interface_ip_details(bridge_name, interface, ips, gateway) @@ -272,7 +281,7 @@ class LinuxBridge: bridge_name, e) return - def add_tap_interface(self, network_id, physical_interface, vlan_id, + def add_tap_interface(self, network_id, physical_network, vlan_id, tap_device_name): """ If a VIF has been plugged into a network, this function will @@ -297,13 +306,25 @@ class LinuxBridge: tap_device_name], root_helper=self.root_helper): return False - if int(vlan_id) == lconst.FLAT_VLAN_ID: - self.ensure_flat_bridge(network_id, physical_interface) + if int(vlan_id) == lconst.LOCAL_VLAN_ID: + self.ensure_local_bridge(network_id) else: - self.ensure_vlan_bridge(network_id, physical_interface, vlan_id) + physical_interface = self.interface_mappings.get(physical_network) + if not physical_interface: + LOG.error("No mapping for physical network %s" % + physical_network) + return False + + if int(vlan_id) == lconst.FLAT_VLAN_ID: + self.ensure_flat_bridge(network_id, physical_interface) + else: + self.ensure_vlan_bridge(network_id, physical_interface, + vlan_id) + if utils.execute(['brctl', 'addif', bridge_name, tap_device_name], root_helper=self.root_helper): return False + LOG.debug("Done adding device %s to bridge %s" % (tap_device_name, bridge_name)) return True @@ -317,19 +338,14 @@ class LinuxBridge: """ return False - physical_interface = self.interface_mappings.get(physical_network) - if not physical_interface: - LOG.error("No mapping for physical network %s" % physical_network) - return False - if interface_id.startswith(GATEWAY_INTERFACE_PREFIX): return self.add_tap_interface(network_id, - physical_interface, vlan_id, + physical_network, vlan_id, interface_id) else: tap_device_name = self.get_tap_device_name(interface_id) return self.add_tap_interface(network_id, - physical_interface, vlan_id, + physical_network, vlan_id, tap_device_name) def delete_vlan_bridge(self, bridge_name): @@ -613,9 +629,18 @@ class LinuxBridgeQuantumAgentRPC: self.setup_rpc(interface_mappings.values()) def setup_rpc(self, physical_interfaces): - # REVISIT try until one succeeds? - mac = utils.get_interface_mac(physical_interfaces[0]) + if physical_interfaces: + mac = utils.get_interface_mac(physical_interfaces[0]) + else: + devices = ip_lib.IPWrapper(self.root_helper).get_devices(True) + if devices: + mac = utils.get_interface_mac(devices[0].name) + else: + LOG.error("Unable to obtain MAC of any device for agent_id") + exit(1) self.agent_id = '%s%s' % ('lb', (mac.replace(":", ""))) + LOG.info("RPC agent_id: %s" % self.agent_id) + self.topic = topics.AGENT self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN) diff --git a/quantum/plugins/linuxbridge/common/config.py b/quantum/plugins/linuxbridge/common/config.py index a73fdca1b..63dae9d6e 100644 --- a/quantum/plugins/linuxbridge/common/config.py +++ b/quantum/plugins/linuxbridge/common/config.py @@ -19,11 +19,14 @@ from quantum.openstack.common import cfg -DEFAULT_VLAN_RANGES = ['default:1000:2999'] -DEFAULT_INTERFACE_MAPPINGS = ['default:eth1'] +DEFAULT_VLAN_RANGES = [] +DEFAULT_INTERFACE_MAPPINGS = [] vlan_opts = [ + cfg.StrOpt('tenant_network_type', default='local', + help="Network type for tenant networks " + "(local, vlan, or none)"), cfg.ListOpt('network_vlan_ranges', default=DEFAULT_VLAN_RANGES, help="List of :: " diff --git a/quantum/plugins/linuxbridge/common/constants.py b/quantum/plugins/linuxbridge/common/constants.py index 87f908898..2ae1412ab 100644 --- a/quantum/plugins/linuxbridge/common/constants.py +++ b/quantum/plugins/linuxbridge/common/constants.py @@ -18,10 +18,10 @@ FLAT_VLAN_ID = -1 +LOCAL_VLAN_ID = -2 -PORT_UP = "ACTIVE" -PORT_DOWN = "DOWN" - -VLANID = 'vlan_id' -PORT_ID = 'port-id' -NET_ID = 'net-id' +# Values for network_type +TYPE_FLAT = 'flat' +TYPE_VLAN = 'vlan' +TYPE_LOCAL = 'local' +TYPE_NONE = 'none' diff --git a/quantum/plugins/linuxbridge/db/l2network_models_v2.py b/quantum/plugins/linuxbridge/db/l2network_models_v2.py index ca840a285..6841cfb90 100644 --- a/quantum/plugins/linuxbridge/db/l2network_models_v2.py +++ b/quantum/plugins/linuxbridge/db/l2network_models_v2.py @@ -45,7 +45,7 @@ class NetworkBinding(model_base.BASEV2): network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', ondelete="CASCADE"), primary_key=True) - physical_network = sa.Column(sa.String(64), nullable=False) + physical_network = sa.Column(sa.String(64)) vlan_id = sa.Column(sa.Integer, nullable=False) def __init__(self, network_id, physical_network, vlan_id): diff --git a/quantum/plugins/linuxbridge/lb_quantum_plugin.py b/quantum/plugins/linuxbridge/lb_quantum_plugin.py index da420e59e..5aeb687d6 100644 --- a/quantum/plugins/linuxbridge/lb_quantum_plugin.py +++ b/quantum/plugins/linuxbridge/lb_quantum_plugin.py @@ -17,7 +17,7 @@ import logging import sys from quantum.api.v2 import attributes -from quantum.common import constants +from quantum.common import constants as q_const from quantum.common import exceptions as q_exc from quantum.common import topics from quantum.db import api as db_api @@ -31,7 +31,7 @@ from quantum.openstack.common import cfg from quantum.openstack.common import rpc from quantum.openstack.common.rpc import dispatcher from quantum.openstack.common.rpc import proxy -from quantum.plugins.linuxbridge.common import constants as lconst +from quantum.plugins.linuxbridge.common import constants from quantum.plugins.linuxbridge.db import l2network_db_v2 as db from quantum import policy @@ -73,7 +73,7 @@ class LinuxBridgeRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin): 'port_id': port['id'], 'admin_state_up': port['admin_state_up']} # Set the port status to UP - db.set_port_status(port['id'], constants.PORT_STATUS_ACTIVE) + db.set_port_status(port['id'], q_const.PORT_STATUS_ACTIVE) else: entry = {'device': device} LOG.debug("%s can not be found in database", device) @@ -90,7 +90,7 @@ class LinuxBridgeRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin): entry = {'device': device, 'exists': True} # Set port status to DOWN - db.set_port_status(port['id'], constants.PORT_STATUS_DOWN) + db.set_port_status(port['id'], q_const.PORT_STATUS_DOWN) else: entry = {'device': device, 'exists': False} @@ -159,6 +159,13 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2, db.initialize() self._parse_network_vlan_ranges() db.sync_network_states(self.network_vlan_ranges) + self.tenant_network_type = cfg.CONF.VLANS.tenant_network_type + if self.tenant_network_type not in [constants.TYPE_LOCAL, + constants.TYPE_VLAN, + constants.TYPE_NONE]: + LOG.error("Invalid tenant_network_type: %s" % + self.tenant_network_type) + sys.exit(1) self.agent_rpc = cfg.CONF.AGENT.rpc self._setup_rpc() LOG.debug("Linux Bridge Plugin initialization complete") @@ -218,12 +225,17 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2, def _extend_network_dict_provider(self, context, network): if self._check_provider_view_auth(context, network): binding = db.get_network_binding(context.session, network['id']) - network[provider.PHYSICAL_NETWORK] = binding.physical_network - if binding.vlan_id == lconst.FLAT_VLAN_ID: - network[provider.NETWORK_TYPE] = 'flat' + if binding.vlan_id == constants.FLAT_VLAN_ID: + network[provider.NETWORK_TYPE] = constants.TYPE_FLAT + network[provider.PHYSICAL_NETWORK] = binding.physical_network + network[provider.SEGMENTATION_ID] = None + elif binding.vlan_id == constants.LOCAL_VLAN_ID: + network[provider.NETWORK_TYPE] = constants.TYPE_LOCAL + network[provider.PHYSICAL_NETWORK] = None network[provider.SEGMENTATION_ID] = None else: - network[provider.NETWORK_TYPE] = 'vlan' + network[provider.NETWORK_TYPE] = constants.TYPE_VLAN + network[provider.PHYSICAL_NETWORK] = binding.physical_network network[provider.SEGMENTATION_ID] = binding.vlan_id def _process_provider_create(self, context, attrs): @@ -245,13 +257,13 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2, if not network_type_set: msg = _("provider:network_type required") raise q_exc.InvalidInput(error_message=msg) - elif network_type == 'flat': + elif network_type == constants.TYPE_FLAT: if segmentation_id_set: msg = _("provider:segmentation_id specified for flat network") raise q_exc.InvalidInput(error_message=msg) else: - segmentation_id = lconst.FLAT_VLAN_ID - elif network_type == 'vlan': + segmentation_id = constants.FLAT_VLAN_ID + elif network_type == constants.TYPE_VLAN: if not segmentation_id_set: msg = _("provider:segmentation_id required") raise q_exc.InvalidInput(error_message=msg) @@ -259,20 +271,34 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2, msg = _("provider:segmentation_id out of range " "(1 through 4094)") raise q_exc.InvalidInput(error_message=msg) + elif network_type == constants.TYPE_LOCAL: + if physical_network_set: + msg = _("provider:physical_network specified for local " + "network") + raise q_exc.InvalidInput(error_message=msg) + else: + physical_network = None + if segmentation_id_set: + msg = _("provider:segmentation_id specified for local " + "network") + raise q_exc.InvalidInput(error_message=msg) + else: + segmentation_id = constants.LOCAL_VLAN_ID else: - msg = _("invalid provider:network_type %s" % network_type) + msg = _("provider:network_type %s not supported" % network_type) raise q_exc.InvalidInput(error_message=msg) - if physical_network_set: - if physical_network not in self.network_vlan_ranges: - msg = _("unknown provider:physical_network %s" % - physical_network) + if network_type in [constants.TYPE_VLAN, constants.TYPE_FLAT]: + if physical_network_set: + if physical_network not in self.network_vlan_ranges: + msg = _("unknown provider:physical_network %s" % + physical_network) + raise q_exc.InvalidInput(error_message=msg) + elif 'default' in self.network_vlan_ranges: + physical_network = 'default' + else: + msg = _("provider:physical_network required") raise q_exc.InvalidInput(error_message=msg) - elif 'default' in self.network_vlan_ranges: - physical_network = 'default' - else: - msg = _("provider:physical_network required") - raise q_exc.InvalidInput(error_message=msg) return (network_type, physical_network, segmentation_id) @@ -303,9 +329,20 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2, session = context.session with session.begin(subtransactions=True): if not network_type: - physical_network, vlan_id = db.reserve_network(session) + # tenant network + network_type = self.tenant_network_type + if network_type == constants.TYPE_NONE: + raise q_exc.TenantNetworksDisabled() + elif network_type == constants.TYPE_VLAN: + physical_network, vlan_id = db.reserve_network(session) + else: # TYPE_LOCAL + vlan_id = constants.LOCAL_VLAN_ID else: - db.reserve_specific_network(session, physical_network, vlan_id) + # provider network + if network_type in [constants.TYPE_VLAN, constants.TYPE_FLAT]: + db.reserve_specific_network(session, physical_network, + vlan_id) + # no reservation needed for TYPE_LOCAL net = super(LinuxBridgePluginV2, self).create_network(context, network) db.add_network_binding(session, net['id'], @@ -334,8 +371,9 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2, binding = db.get_network_binding(session, id) result = super(LinuxBridgePluginV2, self).delete_network(context, id) - db.release_network(session, binding.physical_network, - binding.vlan_id, self.network_vlan_ranges) + if binding.vlan_id != constants.LOCAL_VLAN_ID: + db.release_network(session, binding.physical_network, + binding.vlan_id, self.network_vlan_ranges) # the network_binding record is deleted via cascade from # the network record, so explicit removal is not necessary if self.agent_rpc: diff --git a/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py b/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py index 90d7542c2..dc46d0d07 100755 --- a/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py +++ b/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py @@ -229,7 +229,7 @@ class OVSQuantumAgent(object): '''Provisions a local VLAN. :param net_uuid: the uuid of the network associated with this vlan. - :param network_type: the type of the network ('gre', 'vlan', 'flat') + :param network_type: the network type ('gre', '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' ''' @@ -277,6 +277,9 @@ class OVSQuantumAgent(object): in_port=self.int_ofports[physical_network], dl_vlan=segmentation_id, actions="mod_vlan_vid:%s,normal" % lvid) + elif network_type == constants.TYPE_LOCAL: + # no flows needed for local networks + pass else: LOG.error("provisioning unknown network type %s for net-id=%s" % (network_type, net_uuid)) @@ -311,6 +314,9 @@ class OVSQuantumAgent(object): br = self.int_br br.delete_flows(in_port=self.int_ofports[lvm.physical_network], dl_vlan=lvm.segmentation_id) + elif lvm.network_type == constants.TYPE_LOCAL: + # no flows needed for local networks + pass else: LOG.error("reclaiming unknown network type %s for net-id=%s" % (lvm.network_type, net_uuid)) @@ -325,7 +331,7 @@ class OVSQuantumAgent(object): :param port: a ovslib.VifPort object. :param net_uuid: the net_uuid this port is to be associated with. - :param network_type: the type of the network ('gre', 'vlan', 'flat') + :param network_type: the network type ('gre', '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' ''' diff --git a/quantum/plugins/openvswitch/common/config.py b/quantum/plugins/openvswitch/common/config.py index d89a970eb..194650a57 100644 --- a/quantum/plugins/openvswitch/common/config.py +++ b/quantum/plugins/openvswitch/common/config.py @@ -17,8 +17,8 @@ from quantum.openstack.common import cfg -DEFAULT_BRIDGE_MAPPINGS = ['default:br-eth1'] -DEFAULT_VLAN_RANGES = ['default:1000:2999'] +DEFAULT_BRIDGE_MAPPINGS = [] +DEFAULT_VLAN_RANGES = [] DEFAULT_TUNNEL_RANGES = [] database_opts = [ @@ -34,6 +34,9 @@ ovs_opts = [ cfg.ListOpt('bridge_mappings', default=DEFAULT_BRIDGE_MAPPINGS, help="List of :"), + cfg.StrOpt('tenant_network_type', default='local', + help="Network type for tenant networks " + "(local, vlan, gre, or none)"), cfg.ListOpt('network_vlan_ranges', default=DEFAULT_VLAN_RANGES, help="List of :: " diff --git a/quantum/plugins/openvswitch/common/constants.py b/quantum/plugins/openvswitch/common/constants.py index d23f2cd0f..20a22a8f1 100644 --- a/quantum/plugins/openvswitch/common/constants.py +++ b/quantum/plugins/openvswitch/common/constants.py @@ -23,6 +23,8 @@ TUNNEL = 'tunnel' TYPE_FLAT = 'flat' TYPE_VLAN = 'vlan' TYPE_GRE = 'gre' +TYPE_LOCAL = 'local' +TYPE_NONE = 'none' # Name prefixes for veth device pair linking the integration bridge # with the physical bridge for a physical network diff --git a/quantum/plugins/openvswitch/ovs_models_v2.py b/quantum/plugins/openvswitch/ovs_models_v2.py index fa8c28c40..119bd8c7a 100644 --- a/quantum/plugins/openvswitch/ovs_models_v2.py +++ b/quantum/plugins/openvswitch/ovs_models_v2.py @@ -64,7 +64,8 @@ class NetworkBinding(model_base.BASEV2): network_id = Column(String(36), ForeignKey('networks.id', ondelete="CASCADE"), primary_key=True) - network_type = Column(String(32), nullable=False) # 'gre', 'vlan', 'flat' + # 'gre', 'vlan', 'flat', 'local' + network_type = Column(String(32), nullable=False) physical_network = Column(String(64)) segmentation_id = Column(Integer) # tunnel_id or vlan_id diff --git a/quantum/plugins/openvswitch/ovs_quantum_plugin.py b/quantum/plugins/openvswitch/ovs_quantum_plugin.py index a0c6568db..fde8bd6df 100644 --- a/quantum/plugins/openvswitch/ovs_quantum_plugin.py +++ b/quantum/plugins/openvswitch/ovs_quantum_plugin.py @@ -197,6 +197,14 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2, ovs_db_v2.sync_vlan_allocations(self.network_vlan_ranges) self._parse_tunnel_id_ranges() ovs_db_v2.sync_tunnel_allocations(self.tunnel_id_ranges) + self.tenant_network_type = cfg.CONF.OVS.tenant_network_type + if self.tenant_network_type not in [constants.TYPE_LOCAL, + constants.TYPE_VLAN, + constants.TYPE_GRE, + constants.TYPE_NONE]: + LOG.error("Invalid tenant_network_type: %s" % + self.tenant_network_type) + sys.exit(1) self.agent_rpc = cfg.CONF.AGENT.rpc self.setup_rpc() @@ -280,6 +288,9 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2, elif binding.network_type == constants.TYPE_VLAN: network[provider.PHYSICAL_NETWORK] = binding.physical_network network[provider.SEGMENTATION_ID] = binding.segmentation_id + elif binding.network_type == constants.TYPE_LOCAL: + network[provider.PHYSICAL_NETWORK] = None + network[provider.SEGMENTATION_ID] = None def _process_provider_create(self, context, attrs): network_type = attrs.get(provider.NETWORK_TYPE) @@ -314,20 +325,44 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2, msg = _("provider:segmentation_id out of range " "(1 through 4094)") raise q_exc.InvalidInput(error_message=msg) + elif network_type == constants.TYPE_GRE: + if physical_network_set: + msg = _("provider:physical_network specified for GRE " + "network") + raise q_exc.InvalidInput(error_message=msg) + else: + physical_network = None + if not segmentation_id_set: + msg = _("provider:segmentation_id required") + raise q_exc.InvalidInput(error_message=msg) + elif network_type == constants.TYPE_LOCAL: + if physical_network_set: + msg = _("provider:physical_network specified for local " + "network") + raise q_exc.InvalidInput(error_message=msg) + else: + physical_network = None + if segmentation_id_set: + msg = _("provider:segmentation_id specified for local " + "network") + raise q_exc.InvalidInput(error_message=msg) + else: + segmentation_id = None else: - msg = _("invalid provider:network_type %s" % network_type) + msg = _("provider:network_type %s not supported" % network_type) raise q_exc.InvalidInput(error_message=msg) - if physical_network_set: - if physical_network not in self.network_vlan_ranges: - msg = _("unknown provider:physical_network %s" % - physical_network) + if network_type in [constants.TYPE_VLAN, constants.TYPE_FLAT]: + if physical_network_set: + if physical_network not in self.network_vlan_ranges: + msg = _("unknown provider:physical_network %s" % + physical_network) + raise q_exc.InvalidInput(error_message=msg) + elif 'default' in self.network_vlan_ranges: + physical_network = 'default' + else: + msg = _("provider:physical_network required") raise q_exc.InvalidInput(error_message=msg) - elif 'default' in self.network_vlan_ranges: - physical_network = 'default' - else: - msg = _("provider:physical_network required") - raise q_exc.InvalidInput(error_message=msg) return (network_type, physical_network, segmentation_id) @@ -358,16 +393,24 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2, session = context.session with session.begin(subtransactions=True): if not network_type: - try: + # tenant network + network_type = self.tenant_network_type + if network_type == constants.TYPE_NONE: + raise q_exc.TenantNetworksDisabled() + elif network_type == constants.TYPE_VLAN: (physical_network, segmentation_id) = ovs_db_v2.reserve_vlan(session) - network_type = constants.TYPE_VLAN - except q_exc.NoNetworkAvailable: + elif network_type == constants.TYPE_GRE: segmentation_id = ovs_db_v2.reserve_tunnel(session) - network_type = constants.TYPE_GRE + # no reservation needed for TYPE_LOCAL else: - ovs_db_v2.reserve_specific_vlan(session, physical_network, - segmentation_id) + # provider network + 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: + ovs_db_v2.reserve_specific_tunnel(session, segmentation_id) + # no reservation needed for TYPE_LOCAL net = super(OVSQuantumPluginV2, self).create_network(context, network) ovs_db_v2.add_network_binding(session, net['id'], network_type, @@ -400,7 +443,8 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2, if binding.network_type == constants.TYPE_GRE: ovs_db_v2.release_tunnel(session, binding.segmentation_id, self.tunnel_id_ranges) - else: + elif binding.network_type in [constants.TYPE_VLAN, + constants.TYPE_FLAT]: ovs_db_v2.release_vlan(session, binding.physical_network, binding.segmentation_id, self.network_vlan_ranges) diff --git a/quantum/tests/unit/linuxbridge/test_defaults.py b/quantum/tests/unit/linuxbridge/test_defaults.py index f3361e72a..d2532f853 100644 --- a/quantum/tests/unit/linuxbridge/test_defaults.py +++ b/quantum/tests/unit/linuxbridge/test_defaults.py @@ -32,11 +32,10 @@ class ConfigurationTest(unittest.TestCase): cfg.CONF.AGENT.polling_interval) self.assertEqual('sudo', cfg.CONF.AGENT.root_helper) - - ranges = cfg.CONF.VLANS.network_vlan_ranges - self.assertEqual(1, len(ranges)) - self.assertEqual('default:1000:2999', ranges[0]) - - mappings = cfg.CONF.LINUX_BRIDGE.physical_interface_mappings - self.assertEqual(1, len(mappings)) - self.assertEqual('default:eth1', mappings[0]) + self.assertEqual('local', + cfg.CONF.VLANS.tenant_network_type) + self.assertEqual(0, + len(cfg.CONF.VLANS.network_vlan_ranges)) + self.assertEqual(0, + len(cfg.CONF.LINUX_BRIDGE. + physical_interface_mappings)) diff --git a/quantum/tests/unit/openvswitch/test_ovs_defaults.py b/quantum/tests/unit/openvswitch/test_ovs_defaults.py index c1d09cde3..2a3d4283c 100644 --- a/quantum/tests/unit/openvswitch/test_ovs_defaults.py +++ b/quantum/tests/unit/openvswitch/test_ovs_defaults.py @@ -29,14 +29,7 @@ class ConfigurationTest(unittest.TestCase): self.assertEqual(2, cfg.CONF.DATABASE.reconnect_interval) self.assertEqual(2, cfg.CONF.AGENT.polling_interval) self.assertEqual('sudo', cfg.CONF.AGENT.root_helper) - - mappings = cfg.CONF.OVS.bridge_mappings - self.assertEqual(1, len(mappings)) - self.assertEqual('default:br-eth1', mappings[0]) - - ranges = cfg.CONF.OVS.network_vlan_ranges - self.assertEqual(1, len(ranges)) - self.assertEqual('default:1000:2999', ranges[0]) - - ranges = cfg.CONF.OVS.tunnel_id_ranges - self.assertEqual(0, len(ranges)) + self.assertEqual('local', cfg.CONF.OVS.tenant_network_type) + self.assertEqual(0, len(cfg.CONF.OVS.bridge_mappings)) + self.assertEqual(0, len(cfg.CONF.OVS.network_vlan_ranges)) + self.assertEqual(0, len(cfg.CONF.OVS.tunnel_id_ranges))