Add partial specs support in ML2 for multiprovider extension
ML2 provider network partial specs let admins choose some multiprovider network attributes and let neutron choose remaining attributes. This change provides the implementation for multiprovider networks. In practice, for VLAN/GRE/VXLAN segments provider:segmentation_id choice can be delegated to neutron, in such case neutron try to find a segment in tenant network pools which respects provided segment attributes. For VLAN segments provider:physical_network choice can also be delegated. DocImpact Implements blueprint provider-network-partial-specs Change-Id: I1cf1441a179ec527674276b71e9924841f8570b6
This commit is contained in:
parent
7da4f601ee
commit
66943875e0
@ -32,15 +32,9 @@ class SegmentsContainDuplicateEntry(qexception.InvalidInput):
|
||||
|
||||
|
||||
def _convert_and_validate_segments(segments, valid_values=None):
|
||||
unique = set()
|
||||
for segment in segments:
|
||||
unique.add(tuple(segment.iteritems()))
|
||||
network_type = segment.get(pnet.NETWORK_TYPE,
|
||||
attr.ATTR_NOT_SPECIFIED)
|
||||
segment[pnet.NETWORK_TYPE] = network_type
|
||||
physical_network = segment.get(pnet.PHYSICAL_NETWORK,
|
||||
attr.ATTR_NOT_SPECIFIED)
|
||||
segment[pnet.PHYSICAL_NETWORK] = physical_network
|
||||
segment.setdefault(pnet.NETWORK_TYPE, attr.ATTR_NOT_SPECIFIED)
|
||||
segment.setdefault(pnet.PHYSICAL_NETWORK, attr.ATTR_NOT_SPECIFIED)
|
||||
segmentation_id = segment.get(pnet.SEGMENTATION_ID)
|
||||
if segmentation_id:
|
||||
segment[pnet.SEGMENTATION_ID] = attr.convert_to_int(
|
||||
@ -53,7 +47,21 @@ def _convert_and_validate_segments(segments, valid_values=None):
|
||||
set([pnet.NETWORK_TYPE, pnet.PHYSICAL_NETWORK,
|
||||
pnet.SEGMENTATION_ID])))
|
||||
raise webob.exc.HTTPBadRequest(msg)
|
||||
if len(unique) != len(segments):
|
||||
|
||||
|
||||
def check_duplicate_segments(segments, is_partial_func=None):
|
||||
"""Helper function checking duplicate segments.
|
||||
|
||||
If is_partial_funcs is specified and not None, then
|
||||
SegmentsContainDuplicateEntry is raised if two segments are identical and
|
||||
non partially defined (is_partial_func(segment) == False).
|
||||
Otherwise SegmentsContainDuplicateEntry is raised if two segment are
|
||||
identical.
|
||||
"""
|
||||
if is_partial_func is not None:
|
||||
segments = [s for s in segments if not is_partial_func(s)]
|
||||
fully_specifieds = [tuple(sorted(s.items())) for s in segments]
|
||||
if len(set(fully_specifieds)) != len(fully_specifieds):
|
||||
raise SegmentsContainDuplicateEntry()
|
||||
|
||||
|
||||
|
@ -63,6 +63,14 @@ class TypeDriver(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_partial_segment(self, segment):
|
||||
"""Return True if segment is a partially specified segment.
|
||||
|
||||
:param segment: segment dictionary
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def validate_provider_segment(self, segment):
|
||||
"""Validate attributes of a provider network segment.
|
||||
|
@ -81,6 +81,9 @@ class FlatTypeDriver(api.TypeDriver):
|
||||
def initialize(self):
|
||||
LOG.info(_("ML2 FlatTypeDriver initialization complete"))
|
||||
|
||||
def is_partial_segment(self, segment):
|
||||
return False
|
||||
|
||||
def validate_provider_segment(self, segment):
|
||||
physical_network = segment.get(api.PHYSICAL_NETWORK)
|
||||
if not physical_network:
|
||||
|
@ -40,6 +40,9 @@ class LocalTypeDriver(api.TypeDriver):
|
||||
def initialize(self):
|
||||
pass
|
||||
|
||||
def is_partial_segment(self, segment):
|
||||
return False
|
||||
|
||||
def validate_provider_segment(self, segment):
|
||||
for key, value in segment.iteritems():
|
||||
if value and key not in [api.NETWORK_TYPE]:
|
||||
|
@ -73,6 +73,15 @@ class TypeManager(stevedore.named.NamedExtensionManager):
|
||||
LOG.info(_("Initializing driver for type '%s'"), network_type)
|
||||
driver.obj.initialize()
|
||||
|
||||
def is_partial_segment(self, segment):
|
||||
network_type = segment[api.NETWORK_TYPE]
|
||||
driver = self.drivers.get(network_type)
|
||||
if driver:
|
||||
return driver.obj.is_partial_segment(segment)
|
||||
else:
|
||||
msg = _("network_type value '%s' not supported") % network_type
|
||||
raise exc.InvalidInput(error_message=msg)
|
||||
|
||||
def validate_provider_segment(self, segment):
|
||||
network_type = segment[api.NETWORK_TYPE]
|
||||
driver = self.drivers.get(network_type)
|
||||
|
@ -157,8 +157,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
raise exc.InvalidInput(error_message=msg)
|
||||
|
||||
def _process_provider_create(self, network):
|
||||
segments = []
|
||||
|
||||
if any(attributes.is_attr_set(network.get(f))
|
||||
for f in (provider.NETWORK_TYPE, provider.PHYSICAL_NETWORK,
|
||||
provider.SEGMENTATION_ID)):
|
||||
@ -175,12 +173,14 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
segments = [{provider.NETWORK_TYPE: network_type,
|
||||
provider.PHYSICAL_NETWORK: physical_network,
|
||||
provider.SEGMENTATION_ID: segmentation_id}]
|
||||
return [self._process_provider_segment(s) for s in segments]
|
||||
elif attributes.is_attr_set(network.get(mpnet.SEGMENTS)):
|
||||
segments = network[mpnet.SEGMENTS]
|
||||
else:
|
||||
return
|
||||
|
||||
return [self._process_provider_segment(s) for s in segments]
|
||||
segments = [self._process_provider_segment(s)
|
||||
for s in network[mpnet.SEGMENTS]]
|
||||
mpnet.check_duplicate_segments(
|
||||
segments,
|
||||
self.type_manager.is_partial_segment)
|
||||
return segments
|
||||
|
||||
def _get_attribute(self, attrs, key):
|
||||
value = attrs.get(key)
|
||||
|
@ -745,10 +745,12 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
webob.exc.HTTPBadRequest})
|
||||
|
||||
def _validate_provider_create(self, context, network):
|
||||
if not attr.is_attr_set(network.get(mpnet.SEGMENTS)):
|
||||
segments = network.get(mpnet.SEGMENTS)
|
||||
if not attr.is_attr_set(segments):
|
||||
return
|
||||
|
||||
for segment in network[mpnet.SEGMENTS]:
|
||||
mpnet.check_duplicate_segments(segments)
|
||||
for segment in segments:
|
||||
network_type = segment.get(pnet.NETWORK_TYPE)
|
||||
physical_network = segment.get(pnet.PHYSICAL_NETWORK)
|
||||
segmentation_id = segment.get(pnet.SEGMENTATION_ID)
|
||||
|
@ -326,7 +326,7 @@ class TestMultiSegmentNetworks(Ml2PluginV2TestCase):
|
||||
res = network_req.get_response(self.api)
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_create_network_duplicate_segments(self):
|
||||
def test_create_network_duplicate_full_segments(self):
|
||||
data = {'network': {'name': 'net1',
|
||||
mpnet.SEGMENTS:
|
||||
[{pnet.NETWORK_TYPE: 'vlan',
|
||||
@ -340,6 +340,18 @@ class TestMultiSegmentNetworks(Ml2PluginV2TestCase):
|
||||
res = network_req.get_response(self.api)
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_create_network_duplicate_partial_segments(self):
|
||||
data = {'network': {'name': 'net1',
|
||||
mpnet.SEGMENTS:
|
||||
[{pnet.NETWORK_TYPE: 'vlan',
|
||||
pnet.PHYSICAL_NETWORK: 'physnet1'},
|
||||
{pnet.NETWORK_TYPE: 'vlan',
|
||||
pnet.PHYSICAL_NETWORK: 'physnet1'}],
|
||||
'tenant_id': 'tenant_one'}}
|
||||
network_req = self.new_create_request('networks', data)
|
||||
res = network_req.get_response(self.api)
|
||||
self.assertEqual(201, res.status_int)
|
||||
|
||||
def test_release_segment_no_type_driver(self):
|
||||
segment = {driver_api.NETWORK_TYPE: 'faketype',
|
||||
driver_api.PHYSICAL_NETWORK: 'physnet1',
|
||||
|
@ -40,6 +40,11 @@ class FlatTypeTest(base.BaseTestCase):
|
||||
return session.query(type_flat.FlatAllocation).filter_by(
|
||||
physical_network=segment[api.PHYSICAL_NETWORK]).first()
|
||||
|
||||
def test_is_partial_segment(self):
|
||||
segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT,
|
||||
api.PHYSICAL_NETWORK: 'flat_net1'}
|
||||
self.assertFalse(self.driver.is_partial_segment(segment))
|
||||
|
||||
def test_validate_provider_segment(self):
|
||||
segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT,
|
||||
api.PHYSICAL_NETWORK: 'flat_net1'}
|
||||
|
@ -27,6 +27,10 @@ class LocalTypeTest(base.BaseTestCase):
|
||||
self.driver = type_local.LocalTypeDriver()
|
||||
self.session = None
|
||||
|
||||
def test_is_partial_segment(self):
|
||||
segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL}
|
||||
self.assertFalse(self.driver.is_partial_segment(segment))
|
||||
|
||||
def test_validate_provider_segment(self):
|
||||
segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL}
|
||||
self.driver.validate_provider_segment(segment)
|
||||
|
Loading…
x
Reference in New Issue
Block a user