Add partial specs support in ML2 for vlan provider networks

ML2 provider networks partial specs let admins choose some provider
network attributes and let neutron choose remaining attributes. This
change provides the implementation for VLAN provider networks.

In practice, for VLAN provider networks provider:physical_network
and provider:segmentation_id choices can be delegated to neutron,
in such case neutron will try to find a network in tenant network
pools which respects provided provider attributes.

DocImpact

Related to blueprint provider-network-partial-specs
Partial-Bug: #1330562

Change-Id: I2c52c71167edaa153b2e04681273e2f1be8d03aa
This commit is contained in:
Cedric Brandily 2014-06-16 22:43:13 +02:00
parent 6e877945c9
commit b3202c3283
16 changed files with 424 additions and 99 deletions

View File

@ -179,6 +179,11 @@ class NoNetworkAvailable(ResourceExhausted):
"No tenant network is available for allocation.") "No tenant network is available for allocation.")
class NoNetworkFoundInMaximumAllowedAttempts(ServiceUnavailable):
message = _("Unable to create the network. "
"No available network found in maximum allowed attempts.")
class SubnetMismatchForPort(BadRequest): class SubnetMismatchForPort(BadRequest):
message = _("Subnet on port %(port_id)s does not match " message = _("Subnet on port %(port_id)s does not match "
"the requested subnet %(subnet_id)s") "the requested subnet %(subnet_id)s")

View File

@ -88,7 +88,8 @@ class TypeDriver(object):
"""Reserve resource associated with a provider network segment. """Reserve resource associated with a provider network segment.
:param session: database session :param session: database session
:param segment: segment dictionary using keys defined above :param segment: segment dictionary
:returns: segment dictionary
Called inside transaction context on session to reserve the Called inside transaction context on session to reserve the
type-specific resource for a provider network segment. The type-specific resource for a provider network segment. The

View File

@ -0,0 +1,140 @@
# Copyright (c) 2014 Thales Services SAS
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo.db import exception as db_exc
from neutron.common import exceptions as exc
from neutron.openstack.common import log
from neutron.plugins.ml2 import driver_api as api
# Number of retries to find a valid segment candidate and allocate it
DB_MAX_RETRIES = 10
LOG = log.getLogger(__name__)
class TypeDriverHelper(api.TypeDriver):
"""TypeDriver Helper for segment allocation.
Provide methods helping to perform segment allocation fully or partially
specified.
"""
def __init__(self, model):
self.model = model
self.primary_keys = set(dict(model.__table__.columns))
self.primary_keys.remove("allocated")
def allocate_fully_specified_segment(self, session, **raw_segment):
"""Allocate segment fully specified by raw_segment.
If segment exists, then try to allocate it and return db object
If segment does not exists, then try to create it and return db object
If allocation/creation failed, then return None
"""
network_type = self.get_type()
try:
with session.begin(subtransactions=True):
alloc = (session.query(self.model).filter_by(**raw_segment).
first())
if alloc:
if alloc.allocated:
# Segment already allocated
return
else:
# Segment not allocated
LOG.debug("%(type)s segment %(segment)s allocate "
"started ",
type=network_type, segment=raw_segment)
count = (session.query(self.model).
filter_by(allocated=False, **raw_segment).
update({"allocated": True}))
if count:
LOG.debug("%(type)s segment %(segment)s allocate "
"done ",
type=network_type, segment=raw_segment)
return alloc
# Segment allocated or deleted since select
LOG.debug("%(type)s segment %(segment)s allocate "
"failed: segment has been allocated or "
"deleted",
type=network_type, segment=raw_segment)
# Segment to create or already allocated
LOG.debug("%(type)s segment %(segment)s create started",
type=network_type, segment=raw_segment)
alloc = self.model(allocated=True, **raw_segment)
alloc.save(session)
LOG.debug("%(type)s segment %(segment)s create done",
type=network_type, segment=raw_segment)
except db_exc.DBDuplicateEntry:
# Segment already allocated (insert failure)
alloc = None
LOG.debug("%(type)s segment %(segment)s create failed",
type=network_type, segment=raw_segment)
return alloc
def allocate_partially_specified_segment(self, session, **filters):
"""Allocate model segment from pool partially specified by filters.
Return allocated db object or None.
"""
network_type = self.get_type()
with session.begin(subtransactions=True):
select = (session.query(self.model).
filter_by(allocated=False, **filters))
# Selected segment can be allocated before update by someone else,
# We retry until update success or DB_MAX_RETRIES retries
for attempt in range(1, DB_MAX_RETRIES + 1):
alloc = select.first()
if not alloc:
# No resource available
return
raw_segment = dict((k, alloc[k]) for k in self.primary_keys)
LOG.debug("%(type)s segment allocate from pool, attempt "
"%(attempt)s started with %(segment)s ",
type=network_type, attempt=attempt,
segment=raw_segment)
count = (session.query(self.model).
filter_by(allocated=False, **raw_segment).
update({"allocated": True}))
if count:
LOG.debug("%(type)s segment allocate from pool, attempt "
"%(attempt)s success with %(segment)s ",
type=network_type, attempt=attempt,
segment=raw_segment)
return alloc
# Segment allocated since select
LOG.debug("Allocate %(type)s segment from pool, "
"attempt %(attempt)s failed with segment "
"%(segment)s",
type=network_type, attempt=attempt,
segment=raw_segment)
LOG.warning(_("Allocate %(type)s segment from pool failed "
"after %(number)s failed attempts"),
{"type": network_type, "number": DB_MAX_RETRIES})
raise exc.NoNetworkFoundInMaximumAllowedAttempts

View File

@ -14,6 +14,7 @@
# under the License. # under the License.
from oslo.config import cfg from oslo.config import cfg
from oslo.db import exception as db_exc
import sqlalchemy as sa import sqlalchemy as sa
from neutron.common import exceptions as exc from neutron.common import exceptions as exc
@ -100,17 +101,14 @@ class FlatTypeDriver(api.TypeDriver):
physical_network = segment[api.PHYSICAL_NETWORK] physical_network = segment[api.PHYSICAL_NETWORK]
with session.begin(subtransactions=True): with session.begin(subtransactions=True):
try: try:
alloc = (session.query(FlatAllocation).
filter_by(physical_network=physical_network).
with_lockmode('update').
one())
raise exc.FlatNetworkInUse(
physical_network=physical_network)
except sa.orm.exc.NoResultFound:
LOG.debug(_("Reserving flat network on physical " LOG.debug(_("Reserving flat network on physical "
"network %s"), physical_network) "network %s"), physical_network)
alloc = FlatAllocation(physical_network=physical_network) alloc = FlatAllocation(physical_network=physical_network)
session.add(alloc) alloc.save(session)
except db_exc.DBDuplicateEntry:
raise exc.FlatNetworkInUse(
physical_network=physical_network)
return segment
def allocate_tenant_segment(self, session): def allocate_tenant_segment(self, session):
# Tenant flat networks are not supported. # Tenant flat networks are not supported.

View File

@ -91,6 +91,7 @@ class GreTypeDriver(type_tunnel.TunnelTypeDriver):
alloc = GreAllocation(gre_id=segmentation_id) alloc = GreAllocation(gre_id=segmentation_id)
alloc.allocated = True alloc.allocated = True
session.add(alloc) session.add(alloc)
return segment
def allocate_tenant_segment(self, session): def allocate_tenant_segment(self, session):
with session.begin(subtransactions=True): with session.begin(subtransactions=True):

View File

@ -48,7 +48,7 @@ class LocalTypeDriver(api.TypeDriver):
def reserve_provider_segment(self, session, segment): def reserve_provider_segment(self, session, segment):
# No resources to reserve # No resources to reserve
pass return segment
def allocate_tenant_segment(self, session): def allocate_tenant_segment(self, session):
# No resources to allocate # No resources to allocate

View File

@ -28,6 +28,7 @@ from neutron.openstack.common import log
from neutron.plugins.common import constants as p_const from neutron.plugins.common import constants as p_const
from neutron.plugins.common import utils as plugin_utils from neutron.plugins.common import utils as plugin_utils
from neutron.plugins.ml2 import driver_api as api from neutron.plugins.ml2 import driver_api as api
from neutron.plugins.ml2.drivers import helpers
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
@ -67,7 +68,7 @@ class VlanAllocation(model_base.BASEV2):
allocated = sa.Column(sa.Boolean, nullable=False) allocated = sa.Column(sa.Boolean, nullable=False)
class VlanTypeDriver(api.TypeDriver): class VlanTypeDriver(helpers.TypeDriverHelper):
"""Manage state for VLAN networks with ML2. """Manage state for VLAN networks with ML2.
The VlanTypeDriver implements the 'vlan' network_type. VLAN The VlanTypeDriver implements the 'vlan' network_type. VLAN
@ -79,6 +80,7 @@ class VlanTypeDriver(api.TypeDriver):
""" """
def __init__(self): def __init__(self):
super(VlanTypeDriver, self).__init__(VlanAllocation)
self._parse_network_vlan_ranges() self._parse_network_vlan_ranges()
def _parse_network_vlan_ranges(self): def _parse_network_vlan_ranges(self):
@ -160,25 +162,27 @@ class VlanTypeDriver(api.TypeDriver):
self._sync_vlan_allocations() self._sync_vlan_allocations()
LOG.info(_("VlanTypeDriver initialization complete")) LOG.info(_("VlanTypeDriver initialization complete"))
def is_partial_segment(self, segment):
return segment.get(api.SEGMENTATION_ID) is None
def validate_provider_segment(self, segment): def validate_provider_segment(self, segment):
physical_network = segment.get(api.PHYSICAL_NETWORK) physical_network = segment.get(api.PHYSICAL_NETWORK)
if not physical_network:
msg = _("physical_network required for VLAN provider network")
raise exc.InvalidInput(error_message=msg)
if physical_network not in self.network_vlan_ranges:
msg = (_("physical_network '%s' unknown for VLAN provider network")
% physical_network)
raise exc.InvalidInput(error_message=msg)
segmentation_id = segment.get(api.SEGMENTATION_ID) segmentation_id = segment.get(api.SEGMENTATION_ID)
if segmentation_id is None: if physical_network:
msg = _("segmentation_id required for VLAN provider network") if physical_network not in self.network_vlan_ranges:
raise exc.InvalidInput(error_message=msg) msg = (_("physical_network '%s' unknown "
if not utils.is_valid_vlan_tag(segmentation_id): " for VLAN provider network") % physical_network)
msg = (_("segmentation_id out of range (%(min)s through " raise exc.InvalidInput(error_message=msg)
"%(max)s)") % if segmentation_id:
{'min': q_const.MIN_VLAN_TAG, if not utils.is_valid_vlan_tag(segmentation_id):
'max': q_const.MAX_VLAN_TAG}) msg = (_("segmentation_id out of range (%(min)s through "
"%(max)s)") %
{'min': q_const.MIN_VLAN_TAG,
'max': q_const.MAX_VLAN_TAG})
raise exc.InvalidInput(error_message=msg)
elif segmentation_id:
msg = _("segmentation_id requires physical_network for VLAN "
"provider network")
raise exc.InvalidInput(error_message=msg) raise exc.InvalidInput(error_message=msg)
for key, value in segment.items(): for key, value in segment.items():
@ -189,48 +193,36 @@ class VlanTypeDriver(api.TypeDriver):
raise exc.InvalidInput(error_message=msg) raise exc.InvalidInput(error_message=msg)
def reserve_provider_segment(self, session, segment): def reserve_provider_segment(self, session, segment):
physical_network = segment[api.PHYSICAL_NETWORK] filters = {}
vlan_id = segment[api.SEGMENTATION_ID] physical_network = segment.get(api.PHYSICAL_NETWORK)
with session.begin(subtransactions=True): if physical_network is not None:
try: filters['physical_network'] = physical_network
alloc = (session.query(VlanAllocation). vlan_id = segment.get(api.SEGMENTATION_ID)
filter_by(physical_network=physical_network, if vlan_id is not None:
vlan_id=vlan_id). filters['vlan_id'] = vlan_id
with_lockmode('update').
one()) if self.is_partial_segment(segment):
if alloc.allocated: alloc = self.allocate_partially_specified_segment(
raise exc.VlanIdInUse(vlan_id=vlan_id, session, **filters)
physical_network=physical_network) if not alloc:
LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical " raise exc.NoNetworkAvailable
"network %(physical_network)s from pool"), else:
{'vlan_id': vlan_id, alloc = self.allocate_fully_specified_segment(
'physical_network': physical_network}) session, **filters)
alloc.allocated = True if not alloc:
except sa.orm.exc.NoResultFound: raise exc.VlanIdInUse(**filters)
LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical "
"network %(physical_network)s outside pool"), return {api.NETWORK_TYPE: p_const.TYPE_VLAN,
{'vlan_id': vlan_id, api.PHYSICAL_NETWORK: alloc.physical_network,
'physical_network': physical_network}) api.SEGMENTATION_ID: alloc.vlan_id}
alloc = VlanAllocation(physical_network=physical_network,
vlan_id=vlan_id,
allocated=True)
session.add(alloc)
def allocate_tenant_segment(self, session): def allocate_tenant_segment(self, session):
with session.begin(subtransactions=True): alloc = self.allocate_partially_specified_segment(session)
alloc = (session.query(VlanAllocation). if not alloc:
filter_by(allocated=False). return
with_lockmode('update'). return {api.NETWORK_TYPE: p_const.TYPE_VLAN,
first()) api.PHYSICAL_NETWORK: alloc.physical_network,
if alloc: api.SEGMENTATION_ID: alloc.vlan_id}
LOG.debug(_("Allocating vlan %(vlan_id)s on physical network "
"%(physical_network)s from pool"),
{'vlan_id': alloc.vlan_id,
'physical_network': alloc.physical_network})
alloc.allocated = True
return {api.NETWORK_TYPE: p_const.TYPE_VLAN,
api.PHYSICAL_NETWORK: alloc.physical_network,
api.SEGMENTATION_ID: alloc.vlan_id}
def release_segment(self, session, segment): def release_segment(self, session, segment):
physical_network = segment[api.PHYSICAL_NETWORK] physical_network = segment[api.PHYSICAL_NETWORK]

View File

@ -99,6 +99,7 @@ class VxlanTypeDriver(type_tunnel.TunnelTypeDriver):
alloc = VxlanAllocation(vxlan_vni=segmentation_id) alloc = VxlanAllocation(vxlan_vni=segmentation_id)
alloc.allocated = True alloc.allocated = True
session.add(alloc) session.add(alloc)
return segment
def allocate_tenant_segment(self, session): def allocate_tenant_segment(self, session):
with session.begin(subtransactions=True): with session.begin(subtransactions=True):

View File

@ -85,7 +85,7 @@ class TypeManager(stevedore.named.NamedExtensionManager):
def reserve_provider_segment(self, session, segment): def reserve_provider_segment(self, session, segment):
network_type = segment.get(api.NETWORK_TYPE) network_type = segment.get(api.NETWORK_TYPE)
driver = self.drivers.get(network_type) driver = self.drivers.get(network_type)
driver.obj.reserve_provider_segment(session, segment) return driver.obj.reserve_provider_segment(session, segment)
def allocate_tenant_segment(self, session): def allocate_tenant_segment(self, session):
for network_type in self.tenant_network_types: for network_type in self.tenant_network_types:

View File

@ -378,8 +378,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
# to TypeManager. # to TypeManager.
if segments: if segments:
for segment in segments: for segment in segments:
self.type_manager.reserve_provider_segment(session, segment = self.type_manager.reserve_provider_segment(
segment) session, segment)
db.add_network_segment(session, network_id, segment) db.add_network_segment(session, network_id, segment)
else: else:
segment = self.type_manager.allocate_tenant_segment(session) segment = self.type_manager.allocate_tenant_segment(session)

View File

@ -0,0 +1,141 @@
# Copyright (c) 2014 Thales Services SAS
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from sqlalchemy.orm import query
from neutron.common import exceptions as exc
import neutron.db.api as db
from neutron.plugins.ml2.drivers import helpers
from neutron.plugins.ml2.drivers import type_vlan
from neutron.tests import base
TENANT_NET = 'phys_net2'
VLAN_MIN = 200
VLAN_MAX = 209
VLAN_OUTSIDE = 100
NETWORK_VLAN_RANGES = {
TENANT_NET: [(VLAN_MIN, VLAN_MAX)],
}
class HelpersTest(base.BaseTestCase):
def setUp(self):
super(HelpersTest, self).setUp()
db.configure_db()
self.driver = type_vlan.VlanTypeDriver()
self.driver.network_vlan_ranges = NETWORK_VLAN_RANGES
self.driver._sync_vlan_allocations()
self.session = db.get_session()
self.addCleanup(db.clear_db)
def check_raw_segment(self, expected, observed):
for key, value in expected.items():
self.assertEqual(value, observed[key])
def test_primary_keys(self):
self.assertEqual(set(['physical_network', 'vlan_id']),
self.driver.primary_keys)
def test_allocate_specific_unallocated_segment_in_pools(self):
expected = dict(physical_network=TENANT_NET, vlan_id=VLAN_MIN)
observed = self.driver.allocate_fully_specified_segment(self.session,
**expected)
self.check_raw_segment(expected, observed)
def test_allocate_specific_allocated_segment_in_pools(self):
raw_segment = dict(physical_network=TENANT_NET, vlan_id=VLAN_MIN)
self.driver.allocate_fully_specified_segment(self.session,
**raw_segment)
observed = self.driver.allocate_fully_specified_segment(self.session,
**raw_segment)
self.assertIsNone(observed)
def test_allocate_specific_finally_allocated_segment_in_pools(self):
# Test case: allocate a specific unallocated segment in pools but
# the segment is allocated concurrently between select and update
raw_segment = dict(physical_network=TENANT_NET, vlan_id=VLAN_MIN)
with mock.patch.object(query.Query, 'update', return_value=0):
observed = self.driver.allocate_fully_specified_segment(
self.session, **raw_segment)
self.assertIsNone(observed)
def test_allocate_specific_unallocated_segment_outside_pools(self):
expected = dict(physical_network=TENANT_NET, vlan_id=VLAN_OUTSIDE)
observed = self.driver.allocate_fully_specified_segment(self.session,
**expected)
self.check_raw_segment(expected, observed)
def test_allocate_specific_allocated_segment_outside_pools(self):
raw_segment = dict(physical_network=TENANT_NET, vlan_id=VLAN_OUTSIDE)
self.driver.allocate_fully_specified_segment(self.session,
**raw_segment)
observed = self.driver.allocate_fully_specified_segment(self.session,
**raw_segment)
self.assertIsNone(observed)
def test_allocate_specific_finally_unallocated_segment_outside_pools(self):
# Test case: allocate a specific allocated segment in pools but
# the segment is concurrently unallocated after select or update
expected = dict(physical_network=TENANT_NET, vlan_id=VLAN_MIN)
with mock.patch.object(self.driver.model, 'save'):
observed = self.driver.allocate_fully_specified_segment(
self.session, **expected)
self.check_raw_segment(expected, observed)
def test_allocate_partial_segment_without_filters(self):
expected = dict(physical_network=TENANT_NET)
observed = self.driver.allocate_partially_specified_segment(
self.session)
self.check_raw_segment(expected, observed)
def test_allocate_partial_segment_with_filter(self):
expected = dict(physical_network=TENANT_NET)
observed = self.driver.allocate_partially_specified_segment(
self.session, **expected)
self.check_raw_segment(expected, observed)
def test_allocate_partial_segment_no_resource_available(self):
for i in range(VLAN_MIN, VLAN_MAX + 1):
self.driver.allocate_partially_specified_segment(self.session)
observed = self.driver.allocate_partially_specified_segment(
self.session)
self.assertIsNone(observed)
def test_allocate_partial_segment_outside_pools(self):
raw_segment = dict(physical_network='other_phys_net')
observed = self.driver.allocate_partially_specified_segment(
self.session, **raw_segment)
self.assertIsNone(observed)
def test_allocate_partial_segment_first_attempt_fails(self):
expected = dict(physical_network=TENANT_NET)
with mock.patch.object(query.Query, 'update', side_effect=[0, 1]):
observed = self.driver.allocate_partially_specified_segment(
self.session, **expected)
self.check_raw_segment(expected, observed)
def test_allocate_partial_segment_all_attempts_fail(self):
with mock.patch.object(query.Query, 'update', return_value=0):
with mock.patch.object(helpers.LOG, 'warning') as log_warning:
self.assertRaises(
exc.NoNetworkFoundInMaximumAllowedAttempts,
self.driver.allocate_partially_specified_segment,
self.session)
log_warning.assert_called_once_with(mock.ANY, mock.ANY)

View File

@ -85,8 +85,8 @@ class FlatTypeTest(base.BaseTestCase):
def test_reserve_provider_segment(self): def test_reserve_provider_segment(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT, segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT,
api.PHYSICAL_NETWORK: 'flat_net1'} api.PHYSICAL_NETWORK: 'flat_net1'}
self.driver.reserve_provider_segment(self.session, segment) observed = self.driver.reserve_provider_segment(self.session, segment)
alloc = self._get_allocation(self.session, segment) alloc = self._get_allocation(self.session, observed)
self.assertEqual(segment[api.PHYSICAL_NETWORK], alloc.physical_network) self.assertEqual(segment[api.PHYSICAL_NETWORK], alloc.physical_network)
def test_release_segment(self): def test_release_segment(self):

View File

@ -112,9 +112,9 @@ class GreTypeTest(base.BaseTestCase):
segment = {api.NETWORK_TYPE: 'gre', segment = {api.NETWORK_TYPE: 'gre',
api.PHYSICAL_NETWORK: 'None', api.PHYSICAL_NETWORK: 'None',
api.SEGMENTATION_ID: 101} api.SEGMENTATION_ID: 101}
self.driver.reserve_provider_segment(self.session, segment) observed = self.driver.reserve_provider_segment(self.session, segment)
alloc = self.driver.get_gre_allocation(self.session, alloc = self.driver.get_gre_allocation(self.session,
segment[api.SEGMENTATION_ID]) observed[api.SEGMENTATION_ID])
self.assertTrue(alloc.allocated) self.assertTrue(alloc.allocated)
with testtools.ExpectedException(exc.TunnelIdInUse): with testtools.ExpectedException(exc.TunnelIdInUse):
@ -122,18 +122,18 @@ class GreTypeTest(base.BaseTestCase):
self.driver.release_segment(self.session, segment) self.driver.release_segment(self.session, segment)
alloc = self.driver.get_gre_allocation(self.session, alloc = self.driver.get_gre_allocation(self.session,
segment[api.SEGMENTATION_ID]) observed[api.SEGMENTATION_ID])
self.assertFalse(alloc.allocated) self.assertFalse(alloc.allocated)
segment[api.SEGMENTATION_ID] = 1000 segment[api.SEGMENTATION_ID] = 1000
self.driver.reserve_provider_segment(self.session, segment) observed = self.driver.reserve_provider_segment(self.session, segment)
alloc = self.driver.get_gre_allocation(self.session, alloc = self.driver.get_gre_allocation(self.session,
segment[api.SEGMENTATION_ID]) observed[api.SEGMENTATION_ID])
self.assertTrue(alloc.allocated) self.assertTrue(alloc.allocated)
self.driver.release_segment(self.session, segment) self.driver.release_segment(self.session, segment)
alloc = self.driver.get_gre_allocation(self.session, alloc = self.driver.get_gre_allocation(self.session,
segment[api.SEGMENTATION_ID]) observed[api.SEGMENTATION_ID])
self.assertIsNone(alloc) self.assertIsNone(alloc)
def test_allocate_tenant_segment(self): def test_allocate_tenant_segment(self):

View File

@ -47,8 +47,13 @@ class LocalTypeTest(base.BaseTestCase):
def test_reserve_provider_segment(self): def test_reserve_provider_segment(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL} segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL}
self.driver.reserve_provider_segment(self.session, segment) observed = self.driver.reserve_provider_segment(self.session, segment)
self.driver.release_segment(self.session, segment) self.assertEqual(segment, observed)
def test_release_provider_segment(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL}
observed = self.driver.reserve_provider_segment(self.session, segment)
self.driver.release_segment(self.session, observed)
def test_allocate_tenant_segment(self): def test_allocate_tenant_segment(self):
expected = {api.NETWORK_TYPE: p_const.TYPE_LOCAL} expected = {api.NETWORK_TYPE: p_const.TYPE_LOCAL}

View File

@ -53,12 +53,31 @@ class VlanTypeTest(base.BaseTestCase):
physical_network=segment[api.PHYSICAL_NETWORK], physical_network=segment[api.PHYSICAL_NETWORK],
vlan_id=segment[api.SEGMENTATION_ID]).first() vlan_id=segment[api.SEGMENTATION_ID]).first()
def test_partial_segment_is_partial_segment(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN}
self.assertTrue(self.driver.is_partial_segment(segment))
def test_specific_segment_is_not_partial_segment(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
api.PHYSICAL_NETWORK: PROVIDER_NET,
api.SEGMENTATION_ID: 1}
self.assertFalse(self.driver.is_partial_segment(segment))
def test_validate_provider_segment(self): def test_validate_provider_segment(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
api.PHYSICAL_NETWORK: PROVIDER_NET, api.PHYSICAL_NETWORK: PROVIDER_NET,
api.SEGMENTATION_ID: 1} api.SEGMENTATION_ID: 1}
self.assertIsNone(self.driver.validate_provider_segment(segment)) self.assertIsNone(self.driver.validate_provider_segment(segment))
def test_validate_provider_segment_without_segmentation_id(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
api.PHYSICAL_NETWORK: TENANT_NET}
self.driver.validate_provider_segment(segment)
def test_validate_provider_segment_without_physical_network(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN}
self.driver.validate_provider_segment(segment)
def test_validate_provider_segment_with_missing_physical_network(self): def test_validate_provider_segment_with_missing_physical_network(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
api.SEGMENTATION_ID: 1} api.SEGMENTATION_ID: 1}
@ -66,13 +85,6 @@ class VlanTypeTest(base.BaseTestCase):
self.driver.validate_provider_segment, self.driver.validate_provider_segment,
segment) segment)
def test_validate_provider_segment_with_missing_segmentation_id(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
api.PHYSICAL_NETWORK: PROVIDER_NET}
self.assertRaises(exc.InvalidInput,
self.driver.validate_provider_segment,
segment)
def test_validate_provider_segment_with_invalid_physical_network(self): def test_validate_provider_segment_with_invalid_physical_network(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
api.PHYSICAL_NETWORK: 'other_phys_net', api.PHYSICAL_NETWORK: 'other_phys_net',
@ -129,19 +141,19 @@ class VlanTypeTest(base.BaseTestCase):
api.SEGMENTATION_ID: 101} api.SEGMENTATION_ID: 101}
alloc = self._get_allocation(self.session, segment) alloc = self._get_allocation(self.session, segment)
self.assertIsNone(alloc) self.assertIsNone(alloc)
self.driver.reserve_provider_segment(self.session, segment) observed = self.driver.reserve_provider_segment(self.session, segment)
alloc = self._get_allocation(self.session, segment) alloc = self._get_allocation(self.session, observed)
self.assertTrue(alloc.allocated) self.assertTrue(alloc.allocated)
def test_reserve_provider_segment_already_allocated(self): def test_reserve_provider_segment_already_allocated(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
api.PHYSICAL_NETWORK: PROVIDER_NET, api.PHYSICAL_NETWORK: PROVIDER_NET,
api.SEGMENTATION_ID: 101} api.SEGMENTATION_ID: 101}
self.driver.reserve_provider_segment(self.session, segment) observed = self.driver.reserve_provider_segment(self.session, segment)
self.assertRaises(exc.VlanIdInUse, self.assertRaises(exc.VlanIdInUse,
self.driver.reserve_provider_segment, self.driver.reserve_provider_segment,
self.session, self.session,
segment) observed)
def test_reserve_provider_segment_in_tenant_pools(self): def test_reserve_provider_segment_in_tenant_pools(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN, segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
@ -149,10 +161,39 @@ class VlanTypeTest(base.BaseTestCase):
api.SEGMENTATION_ID: VLAN_MIN} api.SEGMENTATION_ID: VLAN_MIN}
alloc = self._get_allocation(self.session, segment) alloc = self._get_allocation(self.session, segment)
self.assertFalse(alloc.allocated) self.assertFalse(alloc.allocated)
self.driver.reserve_provider_segment(self.session, segment) observed = self.driver.reserve_provider_segment(self.session, segment)
alloc = self._get_allocation(self.session, segment) alloc = self._get_allocation(self.session, observed)
self.assertTrue(alloc.allocated) self.assertTrue(alloc.allocated)
def test_reserve_provider_segment_without_segmentation_id(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
api.PHYSICAL_NETWORK: TENANT_NET}
observed = self.driver.reserve_provider_segment(self.session, segment)
alloc = self._get_allocation(self.session, observed)
self.assertTrue(alloc.allocated)
vlan_id = observed[api.SEGMENTATION_ID]
self.assertThat(vlan_id, matchers.GreaterThan(VLAN_MIN - 1))
self.assertThat(vlan_id, matchers.LessThan(VLAN_MAX + 1))
def test_reserve_provider_segment_without_physical_network(self):
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN}
observed = self.driver.reserve_provider_segment(self.session, segment)
alloc = self._get_allocation(self.session, observed)
self.assertTrue(alloc.allocated)
vlan_id = observed[api.SEGMENTATION_ID]
self.assertThat(vlan_id, matchers.GreaterThan(VLAN_MIN - 1))
self.assertThat(vlan_id, matchers.LessThan(VLAN_MAX + 1))
self.assertEqual(TENANT_NET, observed[api.PHYSICAL_NETWORK])
def test_reserve_provider_segment_all_allocateds(self):
for __ in range(VLAN_MIN, VLAN_MAX + 1):
self.driver.allocate_tenant_segment(self.session)
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN}
self.assertRaises(exc.NoNetworkAvailable,
self.driver.reserve_provider_segment,
self.session,
segment)
def test_allocate_tenant_segment(self): def test_allocate_tenant_segment(self):
for __ in range(VLAN_MIN, VLAN_MAX + 1): for __ in range(VLAN_MIN, VLAN_MAX + 1):
segment = self.driver.allocate_tenant_segment(self.session) segment = self.driver.allocate_tenant_segment(self.session)

View File

@ -120,9 +120,9 @@ class VxlanTypeTest(base.BaseTestCase):
segment = {api.NETWORK_TYPE: 'vxlan', segment = {api.NETWORK_TYPE: 'vxlan',
api.PHYSICAL_NETWORK: 'None', api.PHYSICAL_NETWORK: 'None',
api.SEGMENTATION_ID: 101} api.SEGMENTATION_ID: 101}
self.driver.reserve_provider_segment(self.session, segment) observed = self.driver.reserve_provider_segment(self.session, segment)
alloc = self.driver.get_vxlan_allocation(self.session, alloc = self.driver.get_vxlan_allocation(self.session,
segment[api.SEGMENTATION_ID]) observed[api.SEGMENTATION_ID])
self.assertTrue(alloc.allocated) self.assertTrue(alloc.allocated)
with testtools.ExpectedException(exc.TunnelIdInUse): with testtools.ExpectedException(exc.TunnelIdInUse):
@ -130,18 +130,18 @@ class VxlanTypeTest(base.BaseTestCase):
self.driver.release_segment(self.session, segment) self.driver.release_segment(self.session, segment)
alloc = self.driver.get_vxlan_allocation(self.session, alloc = self.driver.get_vxlan_allocation(self.session,
segment[api.SEGMENTATION_ID]) observed[api.SEGMENTATION_ID])
self.assertFalse(alloc.allocated) self.assertFalse(alloc.allocated)
segment[api.SEGMENTATION_ID] = 1000 segment[api.SEGMENTATION_ID] = 1000
self.driver.reserve_provider_segment(self.session, segment) observed = self.driver.reserve_provider_segment(self.session, segment)
alloc = self.driver.get_vxlan_allocation(self.session, alloc = self.driver.get_vxlan_allocation(self.session,
segment[api.SEGMENTATION_ID]) observed[api.SEGMENTATION_ID])
self.assertTrue(alloc.allocated) self.assertTrue(alloc.allocated)
self.driver.release_segment(self.session, segment) self.driver.release_segment(self.session, segment)
alloc = self.driver.get_vxlan_allocation(self.session, alloc = self.driver.get_vxlan_allocation(self.session,
segment[api.SEGMENTATION_ID]) observed[api.SEGMENTATION_ID])
self.assertIsNone(alloc) self.assertIsNone(alloc)
def test_allocate_tenant_segment(self): def test_allocate_tenant_segment(self):