expose openvswitch GRE tunnel_id via provider API

The provider:vlan_id extended attribute is renamed to
provider:segmentation_id, and the openvswitch plugin returns the
tunnel_id of GRE networks as this attribute.

Fixes bug 1044375. Final patch in series that fixes bug 1037341 and
bug 1035492.

Detailed changes:

- Rename provider:vlan_id to provider:segmentation_id.
- Use consts for providernet extension's attribute names
- Remove validation clause from provider:segmentation_id resource attribute map data.
- Validate provider:segmentation_id in linuxbridge and openvswitch plugins.
- Rename physical_id to segmentation_id in the openvswitch schema, plugin, and agent.

Change-Id: Ic023b30b6e3275955bcb8a09c7936035dacf3f87
This commit is contained in:
Bob Kukura 2012-08-31 00:12:41 -04:00
parent 54bc60850f
commit b10e253dc2
8 changed files with 116 additions and 95 deletions

View File

@ -15,21 +15,24 @@
from quantum.api.v2 import attributes
NETWORK_TYPE = 'provider:network_type'
PHYSICAL_NETWORK = 'provider:physical_network'
SEGMENTATION_ID = 'provider:segmentation_id'
EXTENDED_ATTRIBUTES_2_0 = {
'networks': {
'provider:network_type': {'allow_post': True, 'allow_put': True,
'validate': {'type:values': ['flat',
'vlan']},
'default': attributes.ATTR_NOT_SPECIFIED,
'is_visible': True},
'provider:physical_network': {'allow_post': True, 'allow_put': True,
'default': attributes.ATTR_NOT_SPECIFIED,
'is_visible': True},
'provider:vlan_id': {'allow_post': True, 'allow_put': True,
'convert_to': int,
'validate': {'type:range': (1, 4095)},
'default': attributes.ATTR_NOT_SPECIFIED,
'is_visible': True},
NETWORK_TYPE: {'allow_post': True, 'allow_put': True,
'validate': {'type:values': ['flat',
'vlan']},
'default': attributes.ATTR_NOT_SPECIFIED,
'is_visible': True},
PHYSICAL_NETWORK: {'allow_post': True, 'allow_put': True,
'default': attributes.ATTR_NOT_SPECIFIED,
'is_visible': True},
SEGMENTATION_ID: {'allow_post': True, 'allow_put': True,
'convert_to': int,
'default': attributes.ATTR_NOT_SPECIFIED,
'is_visible': True},
}
}
@ -46,7 +49,7 @@ class Providernet(object):
To create a provider VLAN network using the CLI with admin rights:
(shell) net-create --tenant_id <tenant-id> <net-name> \
--provider:vlan_id <vlan-id>
--provider:segmentation_id <vlan-id>
With admin rights, network dictionaries returned from CLI commands
will also include provider attributes.

View File

@ -25,6 +25,7 @@ from quantum.db import db_base_plugin_v2
from quantum.db import dhcp_rpc_base
from quantum.db import l3_db
from quantum.db import models_v2
from quantum.extensions import providernet as provider
from quantum.openstack.common import context
from quantum.openstack.common import cfg
from quantum.openstack.common import rpc
@ -217,24 +218,25 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
def _extend_network_dict(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
network[provider.PHYSICAL_NETWORK] = binding.physical_network
if binding.vlan_id == lconst.FLAT_VLAN_ID:
network['provider:network_type'] = 'flat'
network['provider:vlan_id'] = None
network[provider.NETWORK_TYPE] = 'flat'
network[provider.SEGMENTATION_ID] = None
else:
network['provider:network_type'] = 'vlan'
network['provider:vlan_id'] = binding.vlan_id
network[provider.NETWORK_TYPE] = 'vlan'
network[provider.SEGMENTATION_ID] = binding.vlan_id
def _process_provider_create(self, context, attrs):
network_type = attrs.get('provider:network_type')
physical_network = attrs.get('provider:physical_network')
vlan_id = attrs.get('provider:vlan_id')
network_type = attrs.get(provider.NETWORK_TYPE)
physical_network = attrs.get(provider.PHYSICAL_NETWORK)
segmentation_id = attrs.get(provider.SEGMENTATION_ID)
network_type_set = attributes.is_attr_set(network_type)
physical_network_set = attributes.is_attr_set(physical_network)
vlan_id_set = attributes.is_attr_set(vlan_id)
segmentation_id_set = attributes.is_attr_set(segmentation_id)
if not (network_type_set or physical_network_set or vlan_id_set):
if not (network_type_set or physical_network_set or
segmentation_id_set):
return (None, None, None)
# Authorize before exposing plugin details to client
@ -244,14 +246,18 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
msg = _("provider:network_type required")
raise q_exc.InvalidInput(error_message=msg)
elif network_type == 'flat':
if vlan_id_set:
msg = _("provider:vlan_id specified for flat network")
if segmentation_id_set:
msg = _("provider:segmentation_id specified for flat network")
raise q_exc.InvalidInput(error_message=msg)
else:
vlan_id = lconst.FLAT_VLAN_ID
segmentation_id = lconst.FLAT_VLAN_ID
elif network_type == 'vlan':
if not vlan_id_set:
msg = _("provider:vlan_id required")
if not segmentation_id_set:
msg = _("provider:segmentation_id required")
raise q_exc.InvalidInput(error_message=msg)
if segmentation_id < 1 or segmentation_id > 4094:
msg = _("provider:segmentation_id out of range "
"(1 through 4094)")
raise q_exc.InvalidInput(error_message=msg)
else:
msg = _("invalid provider:network_type %s" % network_type)
@ -268,18 +274,19 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
msg = _("provider:physical_network required")
raise q_exc.InvalidInput(error_message=msg)
return (network_type, physical_network, vlan_id)
return (network_type, physical_network, segmentation_id)
def _check_provider_update(self, context, attrs):
network_type = attrs.get('provider:network_type')
physical_network = attrs.get('provider:physical_network')
vlan_id = attrs.get('provider:vlan_id')
network_type = attrs.get(provider.NETWORK_TYPE)
physical_network = attrs.get(provider.PHYSICAL_NETWORK)
segmentation_id = attrs.get(provider.SEGMENTATION_ID)
network_type_set = attributes.is_attr_set(network_type)
physical_network_set = attributes.is_attr_set(physical_network)
vlan_id_set = attributes.is_attr_set(vlan_id)
segmentation_id_set = attributes.is_attr_set(segmentation_id)
if not (network_type_set or physical_network_set or vlan_id_set):
if not (network_type_set or physical_network_set or
segmentation_id_set):
return
# Authorize before exposing plugin details to client

View File

@ -51,20 +51,20 @@ DEAD_VLAN_TAG = "4095"
# A class to represent a VIF (i.e., a port that has 'iface-id' and 'vif-mac'
# attributes set).
class LocalVLANMapping:
def __init__(self, vlan, network_type, physical_network, physical_id,
def __init__(self, vlan, network_type, physical_network, segmentation_id,
vif_ids=None):
if vif_ids is None:
vif_ids = []
self.vlan = vlan
self.network_type = network_type
self.physical_network = physical_network
self.physical_id = physical_id
self.segmentation_id = segmentation_id
self.vif_ids = vif_ids
def __str__(self):
return ("lv-id = %s type = %s phys-net = %s phys-id = %s" %
(self.vlan, self.network_type, self.physical_network,
self.physical_id))
self.segmentation_id))
class Port(object):
@ -121,6 +121,8 @@ class OVSRpcCallbacks():
vif_port = self.int_br.get_vif_port_by_id(port['id'])
if vif_port:
if port['admin_state_up']:
# REVISIT(rkukura) This does not seem right. This
# needs to be the local_vlan.
vlan_id = kwargs.get('vlan_id')
# create the networking for the port
self.int_br.set_db_attribute("Port", vif_port.port_name,
@ -236,13 +238,13 @@ class OVSQuantumAgent(object):
consumers)
def provision_local_vlan(self, net_uuid, network_type, physical_network,
physical_id):
segmentation_id):
'''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 physical_network: the physical network for 'vlan' or 'flat'
:param physical_id: the VLAN ID for 'vlan' or tunnel ID for 'tunnel'
:param segmentation_id: the VID for 'vlan' or tunnel ID for 'tunnel'
'''
if not self.available_local_vlans:
@ -251,15 +253,16 @@ class OVSQuantumAgent(object):
LOG.info("Assigning %s as local vlan for net-id=%s" % (lvid, net_uuid))
self.local_vlan_map[net_uuid] = LocalVLANMapping(lvid, network_type,
physical_network,
physical_id)
segmentation_id)
if network_type == constants.TYPE_GRE:
# outbound
self.tun_br.add_flow(priority=4, in_port=self.patch_int_ofport,
dl_vlan=lvid,
actions="set_tunnel:%s,normal" % physical_id)
actions="set_tunnel:%s,normal" %
segmentation_id)
# inbound bcast/mcast
self.tun_br.add_flow(priority=3, tun_id=physical_id,
self.tun_br.add_flow(priority=3, tun_id=segmentation_id,
dl_dst="01:00:00:00:00:00/01:00:00:00:00:00",
actions="mod_vlan_vid:%s,output:%s" %
(lvid, self.patch_int_ofport))
@ -281,11 +284,11 @@ class OVSQuantumAgent(object):
br.add_flow(priority=4,
in_port=self.phys_ofports[physical_network],
dl_vlan=lvid,
actions="mod_vlan_vid:%s,normal" % physical_id)
actions="mod_vlan_vid:%s,normal" % segmentation_id)
# inbound
self.int_br.add_flow(priority=3,
in_port=self.int_ofports[physical_network],
dl_vlan=physical_id,
dl_vlan=segmentation_id,
actions="mod_vlan_vid:%s,normal" % lvid)
else:
LOG.error("provisioning unknown network type %s for net-id=%s" %
@ -300,7 +303,7 @@ class OVSQuantumAgent(object):
LOG.info("reclaming vlan = %s from net-id = %s" % (lvm.vlan, net_uuid))
if lvm.network_type == constants.TYPE_GRE:
self.tun_br.delete_flows(tun_id=lvm.physical_id)
self.tun_br.delete_flows(tun_id=lvm.segmentation_id)
self.tun_br.delete_flows(dl_vlan=lvm.vlan)
elif network_type == constants.TYPE_FLAT:
# outbound
@ -319,7 +322,7 @@ class OVSQuantumAgent(object):
# inbound
br = self.int_br
br.delete_flows(in_port=self.int_ofports[lvm.physical_network],
dl_vlan=lvm.physical_id)
dl_vlan=lvm.segmentation_id)
else:
LOG.error("reclaiming unknown network type %s for net-id=%s" %
(lvm.network_type, net_uuid))
@ -328,7 +331,7 @@ class OVSQuantumAgent(object):
self.available_local_vlans.add(lvm.vlan)
def port_bound(self, port, net_uuid,
network_type, physical_network, physical_id):
network_type, physical_network, segmentation_id):
'''Bind port to net_uuid/lsw_id and install flow for inbound traffic
to vm.
@ -336,17 +339,17 @@ class OVSQuantumAgent(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 physical_network: the physical network for 'vlan' or 'flat'
:param physical_id: the VLAN ID for 'vlan' or tunnel ID for 'tunnel'
:param segmentation_id: the VID for 'vlan' or tunnel ID for 'tunnel'
'''
if net_uuid not in self.local_vlan_map:
self.provision_local_vlan(net_uuid, network_type,
physical_network, physical_id)
physical_network, segmentation_id)
lvm = self.local_vlan_map[net_uuid]
lvm.vif_ids.append(port.vif_id)
if network_type == constants.TYPE_GRE:
# inbound unicast
self.tun_br.add_flow(priority=3, tun_id=physical_id,
self.tun_br.add_flow(priority=3, tun_id=segmentation_id,
dl_dst=port.vif_mac,
actions="mod_vlan_vid:%s,normal" % lvm.vlan)
@ -573,7 +576,7 @@ class OVSQuantumAgent(object):
self.port_bound(p, new_net_uuid,
bind.network_type,
bind.physical_network,
bind.physical_id)
bind.segmentation_id)
all_bindings[p.vif_id].status = (
q_const.PORT_STATUS_ACTIVE)
LOG.info("Port %s on net-id = %s bound to %s " % (
@ -633,7 +636,7 @@ class OVSQuantumAgent(object):
self.port_bound(port, details['network_id'],
details['network_type'],
details['physical_network'],
details['physical_id'])
details['segmentation_id'])
else:
self.port_unbound(port, details['network_id'])
else:

View File

@ -51,11 +51,11 @@ def get_network_binding(session, network_id):
def add_network_binding(session, network_id, network_type,
physical_network, physical_id):
physical_network, segmentation_id):
with session.begin(subtransactions=True):
binding = ovs_models_v2.NetworkBinding(network_id, network_type,
physical_network,
physical_id)
segmentation_id)
session.add(binding)

View File

@ -66,20 +66,20 @@ class NetworkBinding(model_base.BASEV2):
primary_key=True)
network_type = Column(String(32), nullable=False) # 'gre', 'vlan', 'flat'
physical_network = Column(String(64))
physical_id = Column(Integer) # tunnel_id or vlan_id
segmentation_id = Column(Integer) # tunnel_id or vlan_id
def __init__(self, network_id, network_type, physical_network,
physical_id):
segmentation_id):
self.network_id = network_id
self.network_type = network_type
self.physical_network = physical_network
self.physical_id = physical_id
self.segmentation_id = segmentation_id
def __repr__(self):
return "<NetworkBinding(%s,%s,%s,%d)>" % (self.network_id,
self.network_type,
self.physical_network,
self.physical_id)
self.segmentation_id)
class TunnelIP(model_base.BASEV2):

View File

@ -31,6 +31,7 @@ from quantum.common import topics
from quantum.db import db_base_plugin_v2
from quantum.db import dhcp_rpc_base
from quantum.db import l3_db
from quantum.extensions import providernet as provider
from quantum.openstack.common import context
from quantum.openstack.common import cfg
from quantum.openstack.common import rpc
@ -75,7 +76,7 @@ class OVSRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin):
'port_id': port['id'],
'admin_state_up': port['admin_state_up'],
'network_type': binding.network_type,
'physical_id': binding.physical_id,
'segmentation_id': binding.segmentation_id,
'physical_network': binding.physical_network}
# Set the port status to UP
ovs_db_v2.set_port_status(port['id'], q_const.PORT_STATUS_ACTIVE)
@ -266,27 +267,28 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
if self._check_provider_view_auth(context, network):
binding = ovs_db_v2.get_network_binding(context.session,
network['id'])
network['provider:network_type'] = binding.network_type
network[provider.NETWORK_TYPE] = binding.network_type
if binding.network_type == constants.TYPE_GRE:
network['provider:physical_network'] = None
network['provider:vlan_id'] = None
network[provider.PHYSICAL_NETWORK] = None
network[provider.SEGMENTATION_ID] = binding.segmentation_id
elif binding.network_type == constants.TYPE_FLAT:
network['provider:physical_network'] = binding.physical_network
network['provider:vlan_id'] = None
network[provider.PHYSICAL_NETWORK] = binding.physical_network
network[provider.SEGMENTATION_ID] = None
elif binding.network_type == constants.TYPE_VLAN:
network['provider:physical_network'] = binding.physical_network
network['provider:vlan_id'] = binding.physical_id
network[provider.PHYSICAL_NETWORK] = binding.physical_network
network[provider.SEGMENTATION_ID] = binding.segmentation_id
def _process_provider_create(self, context, attrs):
network_type = attrs.get('provider:network_type')
physical_network = attrs.get('provider:physical_network')
vlan_id = attrs.get('provider:vlan_id')
network_type = attrs.get(provider.NETWORK_TYPE)
physical_network = attrs.get(provider.PHYSICAL_NETWORK)
segmentation_id = attrs.get(provider.SEGMENTATION_ID)
network_type_set = attributes.is_attr_set(network_type)
physical_network_set = attributes.is_attr_set(physical_network)
vlan_id_set = attributes.is_attr_set(vlan_id)
segmentation_id_set = attributes.is_attr_set(segmentation_id)
if not (network_type_set or physical_network_set or vlan_id_set):
if not (network_type_set or physical_network_set or
segmentation_id_set):
return (None, None, None)
# Authorize before exposing plugin details to client
@ -296,14 +298,18 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
msg = _("provider:network_type required")
raise q_exc.InvalidInput(error_message=msg)
elif network_type == constants.TYPE_FLAT:
if vlan_id_set:
msg = _("provider:vlan_id specified for flat network")
if segmentation_id_set:
msg = _("provider:segmentation_id specified for flat network")
raise q_exc.InvalidInput(error_message=msg)
else:
vlan_id = constants.FLAT_VLAN_ID
segmentation_id = constants.FLAT_VLAN_ID
elif network_type == constants.TYPE_VLAN:
if not vlan_id_set:
msg = _("provider:vlan_id required")
if not segmentation_id_set:
msg = _("provider:segmentation_id required")
raise q_exc.InvalidInput(error_message=msg)
if segmentation_id < 1 or segmentation_id > 4094:
msg = _("provider:segmentation_id out of range "
"(1 through 4094)")
raise q_exc.InvalidInput(error_message=msg)
else:
msg = _("invalid provider:network_type %s" % network_type)
@ -320,18 +326,19 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
msg = _("provider:physical_network required")
raise q_exc.InvalidInput(error_message=msg)
return (network_type, physical_network, vlan_id)
return (network_type, physical_network, segmentation_id)
def _check_provider_update(self, context, attrs):
network_type = attrs.get('provider:network_type')
physical_network = attrs.get('provider:physical_network')
vlan_id = attrs.get('provider:vlan_id')
network_type = attrs.get(provider.NETWORK_TYPE)
physical_network = attrs.get(provider.PHYSICAL_NETWORK)
segmentation_id = attrs.get(provider.SEGMENTATION_ID)
network_type_set = attributes.is_attr_set(network_type)
physical_network_set = attributes.is_attr_set(physical_network)
vlan_id_set = attributes.is_attr_set(vlan_id)
segmentation_id_set = attributes.is_attr_set(segmentation_id)
if not (network_type_set or physical_network_set or vlan_id_set):
if not (network_type_set or physical_network_set or
segmentation_id_set):
return
# Authorize before exposing plugin details to client
@ -342,26 +349,26 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
def create_network(self, context, network):
(network_type, physical_network,
physical_id) = self._process_provider_create(context,
network['network'])
segmentation_id) = self._process_provider_create(context,
network['network'])
session = context.session
with session.begin(subtransactions=True):
if not network_type:
try:
(physical_network,
physical_id) = ovs_db_v2.reserve_vlan(session)
segmentation_id) = ovs_db_v2.reserve_vlan(session)
network_type = constants.TYPE_VLAN
except q_exc.NoNetworkAvailable:
physical_id = ovs_db_v2.reserve_tunnel(session)
segmentation_id = ovs_db_v2.reserve_tunnel(session)
network_type = constants.TYPE_GRE
else:
ovs_db_v2.reserve_specific_vlan(session, physical_network,
physical_id)
segmentation_id)
net = super(OVSQuantumPluginV2, self).create_network(context,
network)
ovs_db_v2.add_network_binding(session, net['id'], network_type,
physical_network, physical_id)
physical_network, segmentation_id)
self._extend_network_dict(context, net)
# note - exception will rollback entire transaction
LOG.debug("Created network: %s" % net['id'])
@ -384,11 +391,11 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
result = super(OVSQuantumPluginV2, self).delete_network(context,
id)
if binding.network_type == constants.TYPE_GRE:
ovs_db_v2.release_tunnel(session, binding.physical_id,
ovs_db_v2.release_tunnel(session, binding.segmentation_id,
self.tunnel_id_ranges)
else:
ovs_db_v2.release_vlan(session, binding.physical_network,
binding.physical_id,
binding.segmentation_id,
self.network_vlan_ranges)
# the network_binding record is deleted via cascade from
# the network record, so explicit removal is not necessary
@ -418,9 +425,10 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
if original_port['admin_state_up'] != port['admin_state_up']:
binding = ovs_db_v2.get_network_binding(None,
port['network_id'])
# REVISIT(rkukura): needs other binding data as well
# REVISIT(rkukura): Either all binding data or no
# binding data needed.
self.notifier.port_update(self.rpc_context, port,
binding.physical_id)
binding.segmentation_id)
return port
def delete_port(self, context, id, l3_port_check=True):

View File

@ -217,4 +217,4 @@ class NetworkBindingsTest(unittest2.TestCase):
self.assertEqual(binding.network_id, TEST_NETWORK_ID)
self.assertEqual(binding.network_type, 'vlan')
self.assertEqual(binding.physical_network, PHYS_NET)
self.assertEqual(binding.physical_id, 1234)
self.assertEqual(binding.segmentation_id, 1234)

View File

@ -105,7 +105,7 @@ class TunnelTest(unittest.TestCase):
self.mox.VerifyAll()
def testReclaimLocalVlan(self):
self.mock_tun_bridge.delete_flows(tun_id=LVM.physical_id)
self.mock_tun_bridge.delete_flows(tun_id=LVM.segmentation_id)
self.mock_tun_bridge.delete_flows(dl_vlan=LVM.vlan)