Added config variable for External Network type in ML2

Description:
With the ML2 Plugin, every network created has segments with
provider:network_types being tenant_network_types.
When applied to external networks, the types that could be in
tenant_network_types parameter (like vxlan or gre) are not appropriate.

Implementation:
Added new config variable 'external_network_type' in ml2_conf.ini
which contains the default network type for external networks
when no provider attributes are specified, by default it is None.

It also includes small code re-factoring/renaming of import statement.

DocImpact

Closes-Bug: #1328991

Co-Authored-By: Romil Gupta <romilg@hp.com>

Change-Id: Idbbe6bced73cfedbe0f8e7abba35f87589b1a004
This commit is contained in:
Aman Kumar 2015-01-23 01:34:00 -08:00
parent 76a50743c0
commit 26b4e57858
4 changed files with 105 additions and 20 deletions

View File

@ -49,6 +49,15 @@
# Example: physical_network_mtus = physnet1:1550, physnet2:1500
# ======== end of items for MTU selection and advertisement =========
# (StrOpt) Default network type for external networks when no provider
# attributes are specified. By default it is None, which means that if
# provider attributes are not specified while creating external networks
# then they will have the same type as tenant networks.
# Allowed values for external_network_type config option depend on the
# network type values configured in type_drivers config option.
# external_network_type =
# Example: external_network_type = local
[ml2_type_flat]
# (ListOpt) List of physical_network names with which flat networks
# can be created. Use * to allow flat networks with arbitrary

View File

@ -51,6 +51,15 @@ ml2_opts = [
"<physnet>:<mtu val>. This mapping allows "
"specifying a physical network MTU value that "
"differs from the default segment_mtu value.")),
cfg.StrOpt('external_network_type',
help=_("Default network type for external networks when no "
"provider attributes are specified. By default it is "
"None, which means that if provider attributes are not "
"specified while creating external networks then they "
"will have the same type as tenant networks. Allowed "
"values for external_network_type config option depend "
"on the network type values configured in type_drivers "
"config option."))
]

View File

@ -19,6 +19,7 @@ import stevedore
from neutron.api.v2 import attributes
from neutron.common import exceptions as exc
from neutron.extensions import external_net
from neutron.extensions import multiprovidernet as mpnet
from neutron.extensions import portbindings
from neutron.extensions import providernet as provider
@ -49,6 +50,7 @@ class TypeManager(stevedore.named.NamedExtensionManager):
LOG.info(_LI("Loaded type driver names: %s"), self.names())
self._register_types()
self._check_tenant_network_types(cfg.CONF.ml2.tenant_network_types)
self._check_external_network_type(cfg.CONF.ml2.external_network_type)
def _register_types(self):
for ext in self:
@ -75,6 +77,12 @@ class TypeManager(stevedore.named.NamedExtensionManager):
raise SystemExit(1)
LOG.info(_LI("Tenant network_types: %s"), self.tenant_network_types)
def _check_external_network_type(self, ext_network_type):
if ext_network_type and ext_network_type not in self.drivers:
LOG.error(_LE("No type driver for external network_type: %s. "
"Service terminated!"), ext_network_type)
raise SystemExit(1)
def _process_provider_segment(self, segment):
(network_type, physical_network,
segmentation_id) = (self._get_attribute(segment, attr)
@ -102,9 +110,7 @@ class TypeManager(stevedore.named.NamedExtensionManager):
elif attributes.is_attr_set(network.get(mpnet.SEGMENTS)):
segments = [self._process_provider_segment(s)
for s in network[mpnet.SEGMENTS]]
mpnet.check_duplicate_segments(
segments,
self.is_partial_segment)
mpnet.check_duplicate_segments(segments, self.is_partial_segment)
return segments
def _match_segment(self, segment, filters):
@ -162,6 +168,12 @@ class TypeManager(stevedore.named.NamedExtensionManager):
LOG.info(_LI("Initializing driver for type '%s'"), network_type)
driver.obj.initialize()
def _add_network_segment(self, session, network_id, segment, mtu,
segment_index=0):
db.add_network_segment(session, network_id, segment, segment_index)
if segment.get(api.MTU) > 0:
mtu.append(segment[api.MTU])
def create_network_segments(self, context, network, tenant_id):
"""Call type drivers to create network segments."""
segments = self._process_provider_create(network)
@ -173,15 +185,15 @@ class TypeManager(stevedore.named.NamedExtensionManager):
for segment_index, segment in enumerate(segments):
segment = self.reserve_provider_segment(
session, segment)
db.add_network_segment(session, network_id,
segment, segment_index)
if segment.get(api.MTU) > 0:
mtu.append(segment[api.MTU])
self._add_network_segment(session, network_id, segment,
mtu, segment_index)
elif (cfg.CONF.ml2.external_network_type and
self._get_attribute(network, external_net.EXTERNAL)):
segment = self._allocate_ext_net_segment(session)
self._add_network_segment(session, network_id, segment, mtu)
else:
segment = self.allocate_tenant_segment(session)
db.add_network_segment(session, network_id, segment)
if segment.get(api.MTU) > 0:
mtu.append(segment[api.MTU])
segment = self._allocate_tenant_net_segment(session)
self._add_network_segment(session, network_id, segment, mtu)
network[api.MTU] = min(mtu) if mtu else 0
def is_partial_segment(self, segment):
@ -207,14 +219,24 @@ class TypeManager(stevedore.named.NamedExtensionManager):
driver = self.drivers.get(network_type)
return driver.obj.reserve_provider_segment(session, segment)
def allocate_tenant_segment(self, session):
def _allocate_segment(self, session, network_type):
driver = self.drivers.get(network_type)
return driver.obj.allocate_tenant_segment(session)
def _allocate_tenant_net_segment(self, session):
for network_type in self.tenant_network_types:
driver = self.drivers.get(network_type)
segment = driver.obj.allocate_tenant_segment(session)
segment = self._allocate_segment(session, network_type)
if segment:
return segment
raise exc.NoNetworkAvailable()
def _allocate_ext_net_segment(self, session):
network_type = cfg.CONF.ml2.external_network_type
segment = self._allocate_segment(session, network_type)
if segment:
return segment
raise exc.NoNetworkAvailable()
def release_network_segments(self, session, network_id):
segments = db.get_network_segments(session, network_id,
filter_dynamic=None)

View File

@ -33,12 +33,12 @@ from neutron.db import api as db_api
from neutron.db import db_base_plugin_v2 as base_plugin
from neutron.db import l3_db
from neutron.db import models_v2
from neutron.extensions import external_net as external_net
from neutron.extensions import external_net
from neutron.extensions import multiprovidernet as mpnet
from neutron.extensions import portbindings
from neutron.extensions import providernet as pnet
from neutron import manager
from neutron.plugins.common import constants as service_constants
from neutron.plugins.common import constants as p_const
from neutron.plugins.ml2.common import exceptions as ml2_exc
from neutron.plugins.ml2 import config
from neutron.plugins.ml2 import db as ml2_db
@ -267,6 +267,51 @@ class TestMl2NetworksV2(test_plugin.TestNetworksV2,
self.assertEqual(db_api.MAX_RETRIES + 1, f.call_count)
class TestExternalNetwork(Ml2PluginV2TestCase):
def _create_external_network(self):
data = {'network': {'name': 'net1',
'router:external': 'True',
'tenant_id': 'tenant_one'}}
network_req = self.new_create_request('networks', data)
network = self.deserialize(self.fmt,
network_req.get_response(self.api))
return network
def test_external_network_type_none(self):
config.cfg.CONF.set_default('external_network_type',
None,
group='ml2')
network = self._create_external_network()
# For external network, expected network type to be
# tenant_network_types which is by default 'local'.
self.assertEqual(p_const.TYPE_LOCAL,
network['network'][pnet.NETWORK_TYPE])
# No physical network specified, expected 'None'.
self.assertIsNone(network['network'][pnet.PHYSICAL_NETWORK])
# External network will not have a segmentation id.
self.assertIsNone(network['network'][pnet.SEGMENTATION_ID])
# External network will not have multiple segments.
self.assertNotIn(mpnet.SEGMENTS, network['network'])
def test_external_network_type_vlan(self):
config.cfg.CONF.set_default('external_network_type',
p_const.TYPE_VLAN,
group='ml2')
network = self._create_external_network()
# For external network, expected network type to be 'vlan'.
self.assertEqual(p_const.TYPE_VLAN,
network['network'][pnet.NETWORK_TYPE])
# Physical network is expected.
self.assertIsNotNone(network['network'][pnet.PHYSICAL_NETWORK])
# External network will have a segmentation id.
self.assertIsNotNone(network['network'][pnet.SEGMENTATION_ID])
# External network will not have multiple segments.
self.assertNotIn(mpnet.SEGMENTS, network['network'])
class TestMl2SubnetsV2(test_plugin.TestSubnetsV2,
Ml2PluginV2TestCase):
def test_delete_subnet_race_with_dhcp_port_creation(self):
@ -352,7 +397,7 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
def test_l3_cleanup_on_net_delete(self):
l3plugin = manager.NeutronManager.get_service_plugins().get(
service_constants.L3_ROUTER_NAT)
p_const.L3_ROUTER_NAT)
kwargs = {'arg_list': (external_net.EXTERNAL,),
external_net.EXTERNAL: True}
with self.network(**kwargs) as n:
@ -471,7 +516,7 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
ctx = context.get_admin_context()
plugin = manager.NeutronManager.get_plugin()
l3plugin = manager.NeutronManager.get_service_plugins().get(
service_constants.L3_ROUTER_NAT)
p_const.L3_ROUTER_NAT)
with contextlib.nested(
self.port(),
mock.patch.object(l3plugin, 'disassociate_floatingips'),
@ -507,7 +552,7 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
def test_disassociate_floatingips_do_notify_returns_nothing(self):
ctx = context.get_admin_context()
l3plugin = manager.NeutronManager.get_service_plugins().get(
service_constants.L3_ROUTER_NAT)
p_const.L3_ROUTER_NAT)
with self.port() as port:
port_id = port['port']['id']
@ -593,7 +638,7 @@ class TestMl2DvrPortsV2(TestMl2PortsV2):
def test_concurrent_csnat_port_delete(self):
plugin = manager.NeutronManager.get_service_plugins()[
service_constants.L3_ROUTER_NAT]
p_const.L3_ROUTER_NAT]
r = plugin.create_router(
self.context,
{'router': {'name': 'router', 'admin_state_up': True}})