Merge "Add partial specs support in ML2 for vlan provider networks"
This commit is contained in:
commit
ef379f7f97
@ -179,6 +179,11 @@ class NoNetworkAvailable(ResourceExhausted):
|
||||
"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):
|
||||
message = _("Subnet on port %(port_id)s does not match "
|
||||
"the requested subnet %(subnet_id)s")
|
||||
|
@ -88,7 +88,8 @@ class TypeDriver(object):
|
||||
"""Reserve resource associated with a provider network segment.
|
||||
|
||||
: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
|
||||
type-specific resource for a provider network segment. The
|
||||
|
140
neutron/plugins/ml2/drivers/helpers.py
Normal file
140
neutron/plugins/ml2/drivers/helpers.py
Normal 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
|
@ -14,6 +14,7 @@
|
||||
# under the License.
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo.db import exception as db_exc
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.common import exceptions as exc
|
||||
@ -100,17 +101,14 @@ class FlatTypeDriver(api.TypeDriver):
|
||||
physical_network = segment[api.PHYSICAL_NETWORK]
|
||||
with session.begin(subtransactions=True):
|
||||
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 "
|
||||
"network %s"), 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):
|
||||
# Tenant flat networks are not supported.
|
||||
|
@ -93,6 +93,7 @@ class GreTypeDriver(type_tunnel.TunnelTypeDriver):
|
||||
alloc = GreAllocation(gre_id=segmentation_id)
|
||||
alloc.allocated = True
|
||||
session.add(alloc)
|
||||
return segment
|
||||
|
||||
def allocate_tenant_segment(self, session):
|
||||
with session.begin(subtransactions=True):
|
||||
|
@ -48,7 +48,7 @@ class LocalTypeDriver(api.TypeDriver):
|
||||
|
||||
def reserve_provider_segment(self, session, segment):
|
||||
# No resources to reserve
|
||||
pass
|
||||
return segment
|
||||
|
||||
def allocate_tenant_segment(self, session):
|
||||
# No resources to allocate
|
||||
|
@ -28,6 +28,7 @@ from neutron.openstack.common import log
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.common import utils as plugin_utils
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.ml2.drivers import helpers
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
@ -67,7 +68,7 @@ class VlanAllocation(model_base.BASEV2):
|
||||
allocated = sa.Column(sa.Boolean, nullable=False)
|
||||
|
||||
|
||||
class VlanTypeDriver(api.TypeDriver):
|
||||
class VlanTypeDriver(helpers.TypeDriverHelper):
|
||||
"""Manage state for VLAN networks with ML2.
|
||||
|
||||
The VlanTypeDriver implements the 'vlan' network_type. VLAN
|
||||
@ -79,6 +80,7 @@ class VlanTypeDriver(api.TypeDriver):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(VlanTypeDriver, self).__init__(VlanAllocation)
|
||||
self._parse_network_vlan_ranges()
|
||||
|
||||
def _parse_network_vlan_ranges(self):
|
||||
@ -160,25 +162,27 @@ class VlanTypeDriver(api.TypeDriver):
|
||||
self._sync_vlan_allocations()
|
||||
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):
|
||||
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)
|
||||
if segmentation_id is None:
|
||||
msg = _("segmentation_id required for VLAN provider network")
|
||||
raise exc.InvalidInput(error_message=msg)
|
||||
if not utils.is_valid_vlan_tag(segmentation_id):
|
||||
msg = (_("segmentation_id out of range (%(min)s through "
|
||||
"%(max)s)") %
|
||||
{'min': q_const.MIN_VLAN_TAG,
|
||||
'max': q_const.MAX_VLAN_TAG})
|
||||
if physical_network:
|
||||
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)
|
||||
if segmentation_id:
|
||||
if not utils.is_valid_vlan_tag(segmentation_id):
|
||||
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)
|
||||
|
||||
for key, value in segment.items():
|
||||
@ -189,48 +193,36 @@ class VlanTypeDriver(api.TypeDriver):
|
||||
raise exc.InvalidInput(error_message=msg)
|
||||
|
||||
def reserve_provider_segment(self, session, segment):
|
||||
physical_network = segment[api.PHYSICAL_NETWORK]
|
||||
vlan_id = segment[api.SEGMENTATION_ID]
|
||||
with session.begin(subtransactions=True):
|
||||
try:
|
||||
alloc = (session.query(VlanAllocation).
|
||||
filter_by(physical_network=physical_network,
|
||||
vlan_id=vlan_id).
|
||||
with_lockmode('update').
|
||||
one())
|
||||
if alloc.allocated:
|
||||
raise exc.VlanIdInUse(vlan_id=vlan_id,
|
||||
physical_network=physical_network)
|
||||
LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical "
|
||||
"network %(physical_network)s from pool"),
|
||||
{'vlan_id': vlan_id,
|
||||
'physical_network': physical_network})
|
||||
alloc.allocated = True
|
||||
except sa.orm.exc.NoResultFound:
|
||||
LOG.debug(_("Reserving specific vlan %(vlan_id)s on physical "
|
||||
"network %(physical_network)s outside pool"),
|
||||
{'vlan_id': vlan_id,
|
||||
'physical_network': physical_network})
|
||||
alloc = VlanAllocation(physical_network=physical_network,
|
||||
vlan_id=vlan_id,
|
||||
allocated=True)
|
||||
session.add(alloc)
|
||||
filters = {}
|
||||
physical_network = segment.get(api.PHYSICAL_NETWORK)
|
||||
if physical_network is not None:
|
||||
filters['physical_network'] = physical_network
|
||||
vlan_id = segment.get(api.SEGMENTATION_ID)
|
||||
if vlan_id is not None:
|
||||
filters['vlan_id'] = vlan_id
|
||||
|
||||
if self.is_partial_segment(segment):
|
||||
alloc = self.allocate_partially_specified_segment(
|
||||
session, **filters)
|
||||
if not alloc:
|
||||
raise exc.NoNetworkAvailable
|
||||
else:
|
||||
alloc = self.allocate_fully_specified_segment(
|
||||
session, **filters)
|
||||
if not alloc:
|
||||
raise exc.VlanIdInUse(**filters)
|
||||
|
||||
return {api.NETWORK_TYPE: p_const.TYPE_VLAN,
|
||||
api.PHYSICAL_NETWORK: alloc.physical_network,
|
||||
api.SEGMENTATION_ID: alloc.vlan_id}
|
||||
|
||||
def allocate_tenant_segment(self, session):
|
||||
with session.begin(subtransactions=True):
|
||||
alloc = (session.query(VlanAllocation).
|
||||
filter_by(allocated=False).
|
||||
with_lockmode('update').
|
||||
first())
|
||||
if alloc:
|
||||
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}
|
||||
alloc = self.allocate_partially_specified_segment(session)
|
||||
if not alloc:
|
||||
return
|
||||
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):
|
||||
physical_network = segment[api.PHYSICAL_NETWORK]
|
||||
|
@ -101,6 +101,7 @@ class VxlanTypeDriver(type_tunnel.TunnelTypeDriver):
|
||||
alloc = VxlanAllocation(vxlan_vni=segmentation_id)
|
||||
alloc.allocated = True
|
||||
session.add(alloc)
|
||||
return segment
|
||||
|
||||
def allocate_tenant_segment(self, session):
|
||||
with session.begin(subtransactions=True):
|
||||
|
@ -85,7 +85,7 @@ class TypeManager(stevedore.named.NamedExtensionManager):
|
||||
def reserve_provider_segment(self, session, segment):
|
||||
network_type = segment.get(api.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):
|
||||
for network_type in self.tenant_network_types:
|
||||
|
@ -504,8 +504,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
# to TypeManager.
|
||||
if segments:
|
||||
for segment in segments:
|
||||
self.type_manager.reserve_provider_segment(session,
|
||||
segment)
|
||||
segment = self.type_manager.reserve_provider_segment(
|
||||
session, segment)
|
||||
db.add_network_segment(session, network_id, segment)
|
||||
else:
|
||||
segment = self.type_manager.allocate_tenant_segment(session)
|
||||
|
141
neutron/tests/unit/ml2/test_helpers.py
Normal file
141
neutron/tests/unit/ml2/test_helpers.py
Normal 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)
|
@ -85,8 +85,8 @@ class FlatTypeTest(base.BaseTestCase):
|
||||
def test_reserve_provider_segment(self):
|
||||
segment = {api.NETWORK_TYPE: p_const.TYPE_FLAT,
|
||||
api.PHYSICAL_NETWORK: 'flat_net1'}
|
||||
self.driver.reserve_provider_segment(self.session, segment)
|
||||
alloc = self._get_allocation(self.session, segment)
|
||||
observed = self.driver.reserve_provider_segment(self.session, segment)
|
||||
alloc = self._get_allocation(self.session, observed)
|
||||
self.assertEqual(segment[api.PHYSICAL_NETWORK], alloc.physical_network)
|
||||
|
||||
def test_release_segment(self):
|
||||
|
@ -112,9 +112,9 @@ class GreTypeTest(base.BaseTestCase):
|
||||
segment = {api.NETWORK_TYPE: 'gre',
|
||||
api.PHYSICAL_NETWORK: 'None',
|
||||
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,
|
||||
segment[api.SEGMENTATION_ID])
|
||||
observed[api.SEGMENTATION_ID])
|
||||
self.assertTrue(alloc.allocated)
|
||||
|
||||
with testtools.ExpectedException(exc.TunnelIdInUse):
|
||||
@ -122,18 +122,18 @@ class GreTypeTest(base.BaseTestCase):
|
||||
|
||||
self.driver.release_segment(self.session, segment)
|
||||
alloc = self.driver.get_gre_allocation(self.session,
|
||||
segment[api.SEGMENTATION_ID])
|
||||
observed[api.SEGMENTATION_ID])
|
||||
self.assertFalse(alloc.allocated)
|
||||
|
||||
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,
|
||||
segment[api.SEGMENTATION_ID])
|
||||
observed[api.SEGMENTATION_ID])
|
||||
self.assertTrue(alloc.allocated)
|
||||
|
||||
self.driver.release_segment(self.session, segment)
|
||||
alloc = self.driver.get_gre_allocation(self.session,
|
||||
segment[api.SEGMENTATION_ID])
|
||||
observed[api.SEGMENTATION_ID])
|
||||
self.assertIsNone(alloc)
|
||||
|
||||
def test_allocate_tenant_segment(self):
|
||||
|
@ -47,8 +47,13 @@ class LocalTypeTest(base.BaseTestCase):
|
||||
|
||||
def test_reserve_provider_segment(self):
|
||||
segment = {api.NETWORK_TYPE: p_const.TYPE_LOCAL}
|
||||
self.driver.reserve_provider_segment(self.session, segment)
|
||||
self.driver.release_segment(self.session, segment)
|
||||
observed = self.driver.reserve_provider_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):
|
||||
expected = {api.NETWORK_TYPE: p_const.TYPE_LOCAL}
|
||||
|
@ -53,12 +53,31 @@ class VlanTypeTest(base.BaseTestCase):
|
||||
physical_network=segment[api.PHYSICAL_NETWORK],
|
||||
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):
|
||||
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
|
||||
api.PHYSICAL_NETWORK: PROVIDER_NET,
|
||||
api.SEGMENTATION_ID: 1}
|
||||
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):
|
||||
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
|
||||
api.SEGMENTATION_ID: 1}
|
||||
@ -66,13 +85,6 @@ class VlanTypeTest(base.BaseTestCase):
|
||||
self.driver.validate_provider_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):
|
||||
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
|
||||
api.PHYSICAL_NETWORK: 'other_phys_net',
|
||||
@ -129,19 +141,19 @@ class VlanTypeTest(base.BaseTestCase):
|
||||
api.SEGMENTATION_ID: 101}
|
||||
alloc = self._get_allocation(self.session, segment)
|
||||
self.assertIsNone(alloc)
|
||||
self.driver.reserve_provider_segment(self.session, segment)
|
||||
alloc = self._get_allocation(self.session, segment)
|
||||
observed = self.driver.reserve_provider_segment(self.session, segment)
|
||||
alloc = self._get_allocation(self.session, observed)
|
||||
self.assertTrue(alloc.allocated)
|
||||
|
||||
def test_reserve_provider_segment_already_allocated(self):
|
||||
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
|
||||
api.PHYSICAL_NETWORK: PROVIDER_NET,
|
||||
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.driver.reserve_provider_segment,
|
||||
self.session,
|
||||
segment)
|
||||
observed)
|
||||
|
||||
def test_reserve_provider_segment_in_tenant_pools(self):
|
||||
segment = {api.NETWORK_TYPE: p_const.TYPE_VLAN,
|
||||
@ -149,10 +161,39 @@ class VlanTypeTest(base.BaseTestCase):
|
||||
api.SEGMENTATION_ID: VLAN_MIN}
|
||||
alloc = self._get_allocation(self.session, segment)
|
||||
self.assertFalse(alloc.allocated)
|
||||
self.driver.reserve_provider_segment(self.session, segment)
|
||||
alloc = self._get_allocation(self.session, segment)
|
||||
observed = self.driver.reserve_provider_segment(self.session, segment)
|
||||
alloc = self._get_allocation(self.session, observed)
|
||||
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):
|
||||
for __ in range(VLAN_MIN, VLAN_MAX + 1):
|
||||
segment = self.driver.allocate_tenant_segment(self.session)
|
||||
|
@ -120,9 +120,9 @@ class VxlanTypeTest(base.BaseTestCase):
|
||||
segment = {api.NETWORK_TYPE: 'vxlan',
|
||||
api.PHYSICAL_NETWORK: 'None',
|
||||
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,
|
||||
segment[api.SEGMENTATION_ID])
|
||||
observed[api.SEGMENTATION_ID])
|
||||
self.assertTrue(alloc.allocated)
|
||||
|
||||
with testtools.ExpectedException(exc.TunnelIdInUse):
|
||||
@ -130,18 +130,18 @@ class VxlanTypeTest(base.BaseTestCase):
|
||||
|
||||
self.driver.release_segment(self.session, segment)
|
||||
alloc = self.driver.get_vxlan_allocation(self.session,
|
||||
segment[api.SEGMENTATION_ID])
|
||||
observed[api.SEGMENTATION_ID])
|
||||
self.assertFalse(alloc.allocated)
|
||||
|
||||
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,
|
||||
segment[api.SEGMENTATION_ID])
|
||||
observed[api.SEGMENTATION_ID])
|
||||
self.assertTrue(alloc.allocated)
|
||||
|
||||
self.driver.release_segment(self.session, segment)
|
||||
alloc = self.driver.get_vxlan_allocation(self.session,
|
||||
segment[api.SEGMENTATION_ID])
|
||||
observed[api.SEGMENTATION_ID])
|
||||
self.assertIsNone(alloc)
|
||||
|
||||
def test_allocate_tenant_segment(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user