Exhaust VLAN allocations in physnet order

When an operator configured multiple network VLAN ranges, we
had no guarantee that we would actually allocate in the order
specified like we do with segmentation types. In *most* cases,
the natural database order would be that the returned available
segment IDs would come back in the order of the physnets because
the sync_allocations code inserted them in that order. So it
became something some deployments would rely on and operators
would get a surprise when suddenly an allocation would happen
from the later physnets while many VLANs were still available
in the first one.

This patch just strictly makes allocation attempts in the order
of the network_vlan_ranges so operators are always guaranteed that
the later physnets will not automatically be allocated from unless
the earlier physnets are exhausted.

Change-Id: I14ca9b7e1141199f3ec221184fbbe156f1f9e18b
This commit is contained in:
Kevin Benton
2017-04-05 04:06:26 -07:00
parent cb60d32003
commit c5662c86a4
3 changed files with 41 additions and 3 deletions

View File

@@ -16,6 +16,7 @@
Common utilities and helper functions for OpenStack Networking Plugins.
"""
import collections
import contextlib
import hashlib
@@ -131,7 +132,7 @@ def parse_network_vlan_range(network_vlan_range):
def parse_network_vlan_ranges(network_vlan_ranges_cfg_entries):
"""Interpret a list of strings as network[:vlan_begin:vlan_end] entries."""
networks = {}
networks = collections.OrderedDict()
for entry in network_vlan_ranges_cfg_entries:
network, vlan_range = parse_network_vlan_range(entry)
if vlan_range:

View File

@@ -196,8 +196,12 @@ class VlanTypeDriver(helpers.SegmentTypeDriver):
api.MTU: self.get_mtu(alloc.physical_network)}
def allocate_tenant_segment(self, context):
alloc = self.allocate_partially_specified_segment(context)
if not alloc:
for physnet in self.network_vlan_ranges:
alloc = self.allocate_partially_specified_segment(
context, physical_network=physnet)
if alloc:
break
else:
return
return {api.NETWORK_TYPE: p_const.TYPE_VLAN,
api.PHYSICAL_NETWORK: alloc.physical_network,

View File

@@ -259,3 +259,36 @@ class VlanTypeTest(testlib_api.SqlTestCase):
"No vlan_id %(vlan_id)s found on physical network "
"%(physical_network)s",
{'vlan_id': 101, 'physical_network': PROVIDER_NET})
class VlanTypeAllocationTest(testlib_api.SqlTestCase):
def test_allocate_tenant_segment_in_order_of_config(self):
ranges = NETWORK_VLAN_RANGES + ['phys_net3:20:30']
config.cfg.CONF.set_override('network_vlan_ranges',
ranges,
group='ml2_type_vlan')
driver = type_vlan.VlanTypeDriver()
driver.physnet_mtus = []
driver._sync_vlan_allocations()
# swap config order from DB order after sync has happened to
# ensure config order is followed and not DB order
config.cfg.CONF.set_override('network_vlan_ranges',
list(reversed(ranges)),
group='ml2_type_vlan')
driver._parse_network_vlan_ranges()
ctx = context.Context()
for vlan in range(11):
# all of physnet3 should be exhausted first
self.assertEqual(
{'network_type': 'vlan', 'physical_network': 'phys_net3',
'segmentation_id': mock.ANY, 'mtu': 1500},
driver.allocate_tenant_segment(ctx))
for vlan in range(10):
# then physnet2
self.assertEqual(
{'network_type': 'vlan', 'physical_network': 'phys_net2',
'segmentation_id': mock.ANY, 'mtu': 1500},
driver.allocate_tenant_segment(ctx))
# then nothing
self.assertFalse(driver.allocate_tenant_segment(ctx))