Merge "Fix queries to retrieve allocations with network_segment_range"
This commit is contained in:
commit
5bc34d9304
@ -64,3 +64,6 @@ RPC_RES_PROCESSING_STEP = 20
|
|||||||
# IPtables version to support --random-fully option.
|
# IPtables version to support --random-fully option.
|
||||||
# Do not move this constant to neutron-lib, since it is temporary
|
# Do not move this constant to neutron-lib, since it is temporary
|
||||||
IPTABLES_RANDOM_FULLY_VERSION = '1.6.2'
|
IPTABLES_RANDOM_FULLY_VERSION = '1.6.2'
|
||||||
|
|
||||||
|
# Segmentation ID pool; DB select limit to improve the performace.
|
||||||
|
IDPOOL_SELECT_SIZE = 100
|
||||||
|
@ -29,6 +29,14 @@ class GeneveAllocation(model_base.BASEV2):
|
|||||||
def get_segmentation_id(cls):
|
def get_segmentation_id(cls):
|
||||||
return cls.geneve_vni
|
return cls.geneve_vni
|
||||||
|
|
||||||
|
@property
|
||||||
|
def segmentation_id(self):
|
||||||
|
return self.geneve_vni
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def primary_keys():
|
||||||
|
return {'geneve_vni'}
|
||||||
|
|
||||||
|
|
||||||
class GeneveEndpoints(model_base.BASEV2):
|
class GeneveEndpoints(model_base.BASEV2):
|
||||||
"""Represents tunnel endpoint in RPC mode."""
|
"""Represents tunnel endpoint in RPC mode."""
|
||||||
|
@ -31,6 +31,14 @@ class GreAllocation(model_base.BASEV2):
|
|||||||
def get_segmentation_id(cls):
|
def get_segmentation_id(cls):
|
||||||
return cls.gre_id
|
return cls.gre_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def segmentation_id(self):
|
||||||
|
return self.gre_id
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def primary_keys():
|
||||||
|
return {'gre_id'}
|
||||||
|
|
||||||
|
|
||||||
class GreEndpoints(model_base.BASEV2):
|
class GreEndpoints(model_base.BASEV2):
|
||||||
"""Represents tunnel endpoint in RPC mode."""
|
"""Represents tunnel endpoint in RPC mode."""
|
||||||
|
@ -43,3 +43,11 @@ class VlanAllocation(model_base.BASEV2):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def get_segmentation_id(cls):
|
def get_segmentation_id(cls):
|
||||||
return cls.vlan_id
|
return cls.vlan_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def segmentation_id(self):
|
||||||
|
return self.vlan_id
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def primary_keys():
|
||||||
|
return {'vlan_id', 'physical_network'}
|
||||||
|
@ -31,6 +31,14 @@ class VxlanAllocation(model_base.BASEV2):
|
|||||||
def get_segmentation_id(cls):
|
def get_segmentation_id(cls):
|
||||||
return cls.vxlan_vni
|
return cls.vxlan_vni
|
||||||
|
|
||||||
|
@property
|
||||||
|
def segmentation_id(self):
|
||||||
|
return self.vxlan_vni
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def primary_keys():
|
||||||
|
return {'vxlan_vni'}
|
||||||
|
|
||||||
|
|
||||||
class VxlanEndpoints(model_base.BASEV2):
|
class VxlanEndpoints(model_base.BASEV2):
|
||||||
"""Represents tunnel endpoint in RPC mode."""
|
"""Represents tunnel endpoint in RPC mode."""
|
||||||
|
@ -12,6 +12,9 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import itertools
|
||||||
|
|
||||||
from neutron_lib import constants
|
from neutron_lib import constants
|
||||||
from neutron_lib.db import utils as db_utils
|
from neutron_lib.db import utils as db_utils
|
||||||
from neutron_lib import exceptions as n_exc
|
from neutron_lib import exceptions as n_exc
|
||||||
@ -19,8 +22,11 @@ from neutron_lib.objects import common_types
|
|||||||
from oslo_versionedobjects import fields as obj_fields
|
from oslo_versionedobjects import fields as obj_fields
|
||||||
from sqlalchemy import and_
|
from sqlalchemy import and_
|
||||||
from sqlalchemy import not_
|
from sqlalchemy import not_
|
||||||
|
from sqlalchemy import or_
|
||||||
|
from sqlalchemy import sql
|
||||||
|
|
||||||
from neutron._i18n import _
|
from neutron._i18n import _
|
||||||
|
from neutron.common import _constants as common_constants
|
||||||
from neutron.db.models import network_segment_range as range_model
|
from neutron.db.models import network_segment_range as range_model
|
||||||
from neutron.db.models.plugins.ml2 import geneveallocation as \
|
from neutron.db.models.plugins.ml2 import geneveallocation as \
|
||||||
geneve_alloc_model
|
geneve_alloc_model
|
||||||
@ -144,3 +150,83 @@ class NetworkSegmentRange(base.NeutronDbObject):
|
|||||||
models_v2.Network.id)).all()
|
models_v2.Network.id)).all()
|
||||||
return {segmentation_id: project_id
|
return {segmentation_id: project_id
|
||||||
for segmentation_id, project_id in alloc_used}
|
for segmentation_id, project_id in alloc_used}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _build_query_segments(cls, context, model, network_type, **filters):
|
||||||
|
columns = set(dict(model.__table__.columns))
|
||||||
|
model_filters = dict((k, filters[k])
|
||||||
|
for k in columns & set(filters.keys()))
|
||||||
|
query = (context.session.query(model)
|
||||||
|
.filter_by(allocated=False, **model_filters).distinct())
|
||||||
|
_and = and_(
|
||||||
|
cls.db_model.network_type == network_type,
|
||||||
|
model.physical_network == cls.db_model.physical_network if
|
||||||
|
network_type == constants.TYPE_VLAN else sql.expression.true())
|
||||||
|
return query.join(range_model.NetworkSegmentRange, _and)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_segments_for_project(cls, context, model, network_type,
|
||||||
|
model_segmentation_id, **filters):
|
||||||
|
_filters = copy.deepcopy(filters)
|
||||||
|
project_id = _filters.pop('project_id', None)
|
||||||
|
if not project_id:
|
||||||
|
return []
|
||||||
|
|
||||||
|
with cls.db_context_reader(context):
|
||||||
|
query = cls._build_query_segments(context, model, network_type,
|
||||||
|
**_filters)
|
||||||
|
query = query.filter(and_(
|
||||||
|
model_segmentation_id >= cls.db_model.minimum,
|
||||||
|
model_segmentation_id <= cls.db_model.maximum,
|
||||||
|
cls.db_model.project_id == project_id))
|
||||||
|
return query.limit(common_constants.IDPOOL_SELECT_SIZE).all()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_segments_shared(cls, context, model, network_type,
|
||||||
|
model_segmentation_id, **filters):
|
||||||
|
_filters = copy.deepcopy(filters)
|
||||||
|
project_id = _filters.pop('project_id', None)
|
||||||
|
with cls.db_context_reader(context):
|
||||||
|
# Retrieve default segment ID range.
|
||||||
|
default_range = context.session.query(cls.db_model).filter(
|
||||||
|
and_(cls.db_model.network_type == network_type,
|
||||||
|
cls.db_model.default == sql.expression.true()))
|
||||||
|
if network_type == constants.TYPE_VLAN:
|
||||||
|
default_range.filter(cls.db_model.physical_network ==
|
||||||
|
_filters['physical_network'])
|
||||||
|
segment_ids = set(range(default_range.all()[0].minimum,
|
||||||
|
default_range.all()[0].maximum + 1))
|
||||||
|
|
||||||
|
# Retrieve other project segment ID ranges (not own project, not
|
||||||
|
# default range).
|
||||||
|
other_project_ranges = context.session.query(cls.db_model).filter(
|
||||||
|
and_(cls.db_model.project_id != project_id,
|
||||||
|
cls.db_model.project_id.isnot(None),
|
||||||
|
cls.db_model.network_type == network_type))
|
||||||
|
if network_type == constants.TYPE_VLAN:
|
||||||
|
other_project_ranges = other_project_ranges.filter(
|
||||||
|
cls.db_model.physical_network ==
|
||||||
|
_filters['physical_network'])
|
||||||
|
|
||||||
|
for other_project_range in other_project_ranges.all():
|
||||||
|
_set = set(range(other_project_range.minimum,
|
||||||
|
other_project_range.maximum + 1))
|
||||||
|
segment_ids.difference_update(_set)
|
||||||
|
|
||||||
|
# NOTE(ralonsoh): https://stackoverflow.com/questions/4628333/
|
||||||
|
# converting-a-list-of-integers-into-range-in-python
|
||||||
|
segment_ranges = [
|
||||||
|
[t[0][1], t[-1][1]] for t in
|
||||||
|
(tuple(g[1]) for g in itertools.groupby(
|
||||||
|
enumerate(segment_ids),
|
||||||
|
key=lambda enum_seg: enum_seg[1] - enum_seg[0]))]
|
||||||
|
|
||||||
|
# Retrieve all segments belonging to the default range except those
|
||||||
|
# assigned to other projects.
|
||||||
|
query = cls._build_query_segments(context, model, network_type,
|
||||||
|
**_filters)
|
||||||
|
clauses = [and_(model_segmentation_id >= range[0],
|
||||||
|
model_segmentation_id <= range[1])
|
||||||
|
for range in segment_ranges]
|
||||||
|
query = query.filter(or_(*clauses))
|
||||||
|
return query.limit(common_constants.IDPOOL_SELECT_SIZE).all()
|
||||||
|
@ -12,8 +12,11 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import abc
|
||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
|
|
||||||
|
from neutron.common import _constants as common_constants
|
||||||
from neutron.objects import base
|
from neutron.objects import base
|
||||||
|
|
||||||
|
|
||||||
@ -34,3 +37,40 @@ class EndpointBase(base.NeutronDbObject):
|
|||||||
if 'ip_address' in fields:
|
if 'ip_address' in fields:
|
||||||
result['ip_address'] = cls.filter_to_str(result['ip_address'])
|
result['ip_address'] = cls.filter_to_str(result['ip_address'])
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class SegmentAllocation(object, metaclass=abc.ABCMeta):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_unallocated_segments(cls, context, **filters):
|
||||||
|
with cls.db_context_reader(context):
|
||||||
|
columns = set(dict(cls.db_model.__table__.columns))
|
||||||
|
model_filters = dict((k, filters[k])
|
||||||
|
for k in columns & set(filters.keys()))
|
||||||
|
query = context.session.query(cls.db_model).filter_by(
|
||||||
|
allocated=False, **model_filters)
|
||||||
|
return query.limit(common_constants.IDPOOL_SELECT_SIZE).all()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def allocate(cls, context, **segment):
|
||||||
|
with cls.db_context_writer(context):
|
||||||
|
return context.session.query(cls.db_model).filter_by(
|
||||||
|
allocated=False, **segment).update({'allocated': True})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def deallocate(cls, context, **segment):
|
||||||
|
with cls.db_context_writer(context):
|
||||||
|
return context.session.query(cls.db_model).filter_by(
|
||||||
|
allocated=True, **segment).update({'allocated': False})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_primary_keys(cls, _dict, segmentation_id=None, **kwargs):
|
||||||
|
_dict[cls.primary_keys[0]] = segmentation_id
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_segmentation_id(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def segmentation_id(self):
|
||||||
|
return self.db_obj.segmentation_id
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from neutron_lib import constants as n_const
|
||||||
from oslo_versionedobjects import fields as obj_fields
|
from oslo_versionedobjects import fields as obj_fields
|
||||||
|
|
||||||
from neutron.db.models.plugins.ml2 import flatallocation
|
from neutron.db.models.plugins.ml2 import flatallocation
|
||||||
@ -29,3 +29,5 @@ class FlatAllocation(base.NeutronDbObject):
|
|||||||
}
|
}
|
||||||
|
|
||||||
primary_keys = ['physical_network']
|
primary_keys = ['physical_network']
|
||||||
|
|
||||||
|
network_type = n_const.TYPE_FLAT
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from neutron_lib import constants as n_const
|
||||||
from oslo_versionedobjects import fields as obj_fields
|
from oslo_versionedobjects import fields as obj_fields
|
||||||
|
|
||||||
from neutron.db.models.plugins.ml2 import geneveallocation
|
from neutron.db.models.plugins.ml2 import geneveallocation
|
||||||
@ -20,7 +21,7 @@ from neutron.objects.plugins.ml2 import base as ml2_base
|
|||||||
|
|
||||||
|
|
||||||
@base.NeutronObjectRegistry.register
|
@base.NeutronObjectRegistry.register
|
||||||
class GeneveAllocation(base.NeutronDbObject):
|
class GeneveAllocation(base.NeutronDbObject, ml2_base.SegmentAllocation):
|
||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
VERSION = '1.0'
|
VERSION = '1.0'
|
||||||
|
|
||||||
@ -33,6 +34,12 @@ class GeneveAllocation(base.NeutronDbObject):
|
|||||||
'allocated': obj_fields.BooleanField(default=False),
|
'allocated': obj_fields.BooleanField(default=False),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
network_type = n_const.TYPE_GENEVE
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_segmentation_id(cls):
|
||||||
|
return cls.db_model.get_segmentation_id()
|
||||||
|
|
||||||
|
|
||||||
@base.NeutronObjectRegistry.register
|
@base.NeutronObjectRegistry.register
|
||||||
class GeneveEndpoint(ml2_base.EndpointBase):
|
class GeneveEndpoint(ml2_base.EndpointBase):
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from neutron_lib import constants as n_const
|
||||||
from oslo_versionedobjects import fields as obj_fields
|
from oslo_versionedobjects import fields as obj_fields
|
||||||
|
|
||||||
from neutron.db.models.plugins.ml2 import gre_allocation_endpoints as gre_model
|
from neutron.db.models.plugins.ml2 import gre_allocation_endpoints as gre_model
|
||||||
@ -20,7 +21,7 @@ from neutron.objects.plugins.ml2 import base as ml2_base
|
|||||||
|
|
||||||
|
|
||||||
@base.NeutronObjectRegistry.register
|
@base.NeutronObjectRegistry.register
|
||||||
class GreAllocation(base.NeutronDbObject):
|
class GreAllocation(base.NeutronDbObject, ml2_base.SegmentAllocation):
|
||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
VERSION = '1.0'
|
VERSION = '1.0'
|
||||||
|
|
||||||
@ -33,6 +34,12 @@ class GreAllocation(base.NeutronDbObject):
|
|||||||
'allocated': obj_fields.BooleanField(default=False)
|
'allocated': obj_fields.BooleanField(default=False)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
network_type = n_const.TYPE_GRE
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_segmentation_id(cls):
|
||||||
|
return cls.db_model.get_segmentation_id()
|
||||||
|
|
||||||
|
|
||||||
@base.NeutronObjectRegistry.register
|
@base.NeutronObjectRegistry.register
|
||||||
class GreEndpoint(ml2_base.EndpointBase):
|
class GreEndpoint(ml2_base.EndpointBase):
|
||||||
|
@ -12,15 +12,17 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from neutron_lib import constants as n_const
|
||||||
from neutron_lib.objects import common_types
|
from neutron_lib.objects import common_types
|
||||||
from oslo_versionedobjects import fields as obj_fields
|
from oslo_versionedobjects import fields as obj_fields
|
||||||
|
|
||||||
from neutron.db.models.plugins.ml2 import vlanallocation as vlan_alloc_model
|
from neutron.db.models.plugins.ml2 import vlanallocation as vlan_alloc_model
|
||||||
from neutron.objects import base
|
from neutron.objects import base
|
||||||
|
from neutron.objects.plugins.ml2 import base as ml2_base
|
||||||
|
|
||||||
|
|
||||||
@base.NeutronObjectRegistry.register
|
@base.NeutronObjectRegistry.register
|
||||||
class VlanAllocation(base.NeutronDbObject):
|
class VlanAllocation(base.NeutronDbObject, ml2_base.SegmentAllocation):
|
||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
VERSION = '1.0'
|
VERSION = '1.0'
|
||||||
|
|
||||||
@ -34,6 +36,8 @@ class VlanAllocation(base.NeutronDbObject):
|
|||||||
|
|
||||||
primary_keys = ['physical_network', 'vlan_id']
|
primary_keys = ['physical_network', 'vlan_id']
|
||||||
|
|
||||||
|
network_type = n_const.TYPE_VLAN
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_physical_networks(context):
|
def get_physical_networks(context):
|
||||||
query = context.session.query(VlanAllocation.db_model.physical_network)
|
query = context.session.query(VlanAllocation.db_model.physical_network)
|
||||||
@ -54,3 +58,13 @@ class VlanAllocation(base.NeutronDbObject):
|
|||||||
[{'physical_network': physical_network,
|
[{'physical_network': physical_network,
|
||||||
'allocated': False,
|
'allocated': False,
|
||||||
'vlan_id': vlan_id} for vlan_id in vlan_ids])
|
'vlan_id': vlan_id} for vlan_id in vlan_ids])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_primary_keys(cls, _dict, segmentation_id=None,
|
||||||
|
physical_network=None):
|
||||||
|
_dict['physical_network'] = physical_network
|
||||||
|
_dict['vlan_id'] = segmentation_id
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_segmentation_id(cls):
|
||||||
|
return cls.db_model.get_segmentation_id()
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from neutron_lib import constants as n_const
|
||||||
from neutron_lib.objects import common_types
|
from neutron_lib.objects import common_types
|
||||||
from oslo_versionedobjects import fields as obj_fields
|
from oslo_versionedobjects import fields as obj_fields
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ from neutron.objects.plugins.ml2 import base as ml2_base
|
|||||||
|
|
||||||
|
|
||||||
@base.NeutronObjectRegistry.register
|
@base.NeutronObjectRegistry.register
|
||||||
class VxlanAllocation(base.NeutronDbObject):
|
class VxlanAllocation(base.NeutronDbObject, ml2_base.SegmentAllocation):
|
||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
VERSION = '1.0'
|
VERSION = '1.0'
|
||||||
|
|
||||||
@ -34,6 +35,12 @@ class VxlanAllocation(base.NeutronDbObject):
|
|||||||
'allocated': obj_fields.BooleanField(default=False),
|
'allocated': obj_fields.BooleanField(default=False),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
network_type = n_const.TYPE_VXLAN
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_segmentation_id(cls):
|
||||||
|
return cls.db_model.get_segmentation_id()
|
||||||
|
|
||||||
|
|
||||||
@base.NeutronObjectRegistry.register
|
@base.NeutronObjectRegistry.register
|
||||||
class VxlanEndpoint(ml2_base.EndpointBase):
|
class VxlanEndpoint(ml2_base.EndpointBase):
|
||||||
|
@ -13,10 +13,8 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import random
|
import functools
|
||||||
|
|
||||||
from neutron_lib import constants as p_const
|
|
||||||
from neutron_lib import context as neutron_ctx
|
|
||||||
from neutron_lib.db import api as db_api
|
from neutron_lib.db import api as db_api
|
||||||
from neutron_lib import exceptions
|
from neutron_lib import exceptions
|
||||||
from neutron_lib.plugins import constants as plugin_constants
|
from neutron_lib.plugins import constants as plugin_constants
|
||||||
@ -27,17 +25,12 @@ from neutron_lib.utils import helpers
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_db import exception as db_exc
|
from oslo_db import exception as db_exc
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from sqlalchemy import and_
|
|
||||||
from sqlalchemy import sql
|
|
||||||
|
|
||||||
from neutron.db.models import network_segment_range as range_model
|
from neutron.objects import network_segment_range as ns_range
|
||||||
from neutron.objects import base as base_obj
|
|
||||||
|
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
IDPOOL_SELECT_SIZE = 100
|
|
||||||
|
|
||||||
|
|
||||||
class BaseTypeDriver(api.ML2TypeDriver):
|
class BaseTypeDriver(api.ML2TypeDriver):
|
||||||
"""BaseTypeDriver for functions common to Segment and flat."""
|
"""BaseTypeDriver for functions common to Segment and flat."""
|
||||||
@ -64,61 +57,10 @@ class SegmentTypeDriver(BaseTypeDriver):
|
|||||||
|
|
||||||
def __init__(self, model):
|
def __init__(self, model):
|
||||||
super(SegmentTypeDriver, self).__init__()
|
super(SegmentTypeDriver, self).__init__()
|
||||||
if issubclass(model, base_obj.NeutronDbObject):
|
self.model = model.db_model
|
||||||
self.model = model.db_model
|
self.segmentation_obj = model
|
||||||
else:
|
primary_keys_columns = self.model.__table__.primary_key.columns
|
||||||
self.model = model
|
self.primary_keys = {col.name for col in primary_keys_columns}
|
||||||
self.primary_keys = set(dict(self.model.__table__.columns))
|
|
||||||
self.primary_keys.remove("allocated")
|
|
||||||
|
|
||||||
# TODO(ataraday): get rid of this method when old TypeDriver won't be used
|
|
||||||
def _get_session(self, arg):
|
|
||||||
if isinstance(arg, neutron_ctx.Context):
|
|
||||||
return arg.session, db_api.CONTEXT_WRITER.using(arg)
|
|
||||||
return arg, arg.session.begin(subtransactions=True)
|
|
||||||
|
|
||||||
def build_segment_query(self, session, **filters):
|
|
||||||
# Only uses filters that correspond to columns defined by this model.
|
|
||||||
# Subclasses may use/support additional filters
|
|
||||||
columns = set(dict(self.model.__table__.columns))
|
|
||||||
model_filters = dict((k, filters[k])
|
|
||||||
for k in columns & set(filters.keys()))
|
|
||||||
return [session.query(self.model).filter_by(allocated=False,
|
|
||||||
**model_filters)]
|
|
||||||
|
|
||||||
def build_segment_queries_for_tenant_and_shared_ranges(self, session,
|
|
||||||
**filters):
|
|
||||||
"""Enforces that segments are allocated from network segment ranges
|
|
||||||
that are owned by the tenant, and then from shared ranges, but never
|
|
||||||
from ranges owned by other tenants.
|
|
||||||
This method also enforces that other network segment range attributes
|
|
||||||
are used when constraining the set of possible segments to be used.
|
|
||||||
"""
|
|
||||||
network_type = self.get_type()
|
|
||||||
project_id = filters.pop('project_id', None)
|
|
||||||
columns = set(dict(self.model.__table__.columns))
|
|
||||||
model_filters = dict((k, filters[k])
|
|
||||||
for k in columns & set(filters.keys()))
|
|
||||||
query = (session.query(self.model)
|
|
||||||
.filter_by(allocated=False, **model_filters))
|
|
||||||
query = query.join(
|
|
||||||
range_model.NetworkSegmentRange,
|
|
||||||
and_(range_model.NetworkSegmentRange.network_type == network_type,
|
|
||||||
self.model.physical_network ==
|
|
||||||
range_model.NetworkSegmentRange.physical_network if
|
|
||||||
network_type == p_const.TYPE_VLAN else
|
|
||||||
sql.expression.true()))
|
|
||||||
query = query.filter(and_(self.model_segmentation_id >=
|
|
||||||
range_model.NetworkSegmentRange.minimum,
|
|
||||||
self.model_segmentation_id <=
|
|
||||||
range_model.NetworkSegmentRange.maximum))
|
|
||||||
query_project_id = (query.filter(
|
|
||||||
range_model.NetworkSegmentRange.project_id == project_id) if
|
|
||||||
project_id is not None else [])
|
|
||||||
query_shared = query.filter(
|
|
||||||
range_model.NetworkSegmentRange.shared == sql.expression.true())
|
|
||||||
|
|
||||||
return [query_project_id] + [query_shared]
|
|
||||||
|
|
||||||
def allocate_fully_specified_segment(self, context, **raw_segment):
|
def allocate_fully_specified_segment(self, context, **raw_segment):
|
||||||
"""Allocate segment fully specified by raw_segment.
|
"""Allocate segment fully specified by raw_segment.
|
||||||
@ -129,12 +71,10 @@ class SegmentTypeDriver(BaseTypeDriver):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
network_type = self.get_type()
|
network_type = self.get_type()
|
||||||
session, ctx_manager = self._get_session(context)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with ctx_manager:
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
alloc = (
|
alloc = (
|
||||||
session.query(self.model).filter_by(**raw_segment).
|
context.session.query(self.model).filter_by(**raw_segment).
|
||||||
first())
|
first())
|
||||||
if alloc:
|
if alloc:
|
||||||
if alloc.allocated:
|
if alloc.allocated:
|
||||||
@ -146,7 +86,7 @@ class SegmentTypeDriver(BaseTypeDriver):
|
|||||||
"started ",
|
"started ",
|
||||||
{"type": network_type,
|
{"type": network_type,
|
||||||
"segment": raw_segment})
|
"segment": raw_segment})
|
||||||
count = (session.query(self.model).
|
count = (context.session.query(self.model).
|
||||||
filter_by(allocated=False, **raw_segment).
|
filter_by(allocated=False, **raw_segment).
|
||||||
update({"allocated": True}))
|
update({"allocated": True}))
|
||||||
if count:
|
if count:
|
||||||
@ -167,7 +107,7 @@ class SegmentTypeDriver(BaseTypeDriver):
|
|||||||
LOG.debug("%(type)s segment %(segment)s create started",
|
LOG.debug("%(type)s segment %(segment)s create started",
|
||||||
{"type": network_type, "segment": raw_segment})
|
{"type": network_type, "segment": raw_segment})
|
||||||
alloc = self.model(allocated=True, **raw_segment)
|
alloc = self.model(allocated=True, **raw_segment)
|
||||||
alloc.save(session)
|
alloc.save(context.session)
|
||||||
LOG.debug("%(type)s segment %(segment)s create done",
|
LOG.debug("%(type)s segment %(segment)s create done",
|
||||||
{"type": network_type, "segment": raw_segment})
|
{"type": network_type, "segment": raw_segment})
|
||||||
|
|
||||||
@ -184,46 +124,30 @@ class SegmentTypeDriver(BaseTypeDriver):
|
|||||||
|
|
||||||
Return allocated db object or None.
|
Return allocated db object or None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
network_type = self.get_type()
|
network_type = self.get_type()
|
||||||
session, ctx_manager = self._get_session(context)
|
if directory.get_plugin(plugin_constants.NETWORK_SEGMENT_RANGE):
|
||||||
with ctx_manager:
|
calls = [
|
||||||
queries = (self.build_segment_queries_for_tenant_and_shared_ranges(
|
functools.partial(
|
||||||
session, **filters)
|
ns_range.NetworkSegmentRange.get_segments_for_project,
|
||||||
if directory.get_plugin(
|
context, self.model, network_type,
|
||||||
plugin_constants.NETWORK_SEGMENT_RANGE) else
|
self.model_segmentation_id, **filters),
|
||||||
self.build_segment_query(session, **filters))
|
functools.partial(
|
||||||
|
ns_range.NetworkSegmentRange.get_segments_shared,
|
||||||
|
context, self.model, network_type,
|
||||||
|
self.model_segmentation_id, **filters)]
|
||||||
|
else:
|
||||||
|
calls = [functools.partial(
|
||||||
|
self.segmentation_obj.get_unallocated_segments,
|
||||||
|
context, **filters)]
|
||||||
|
|
||||||
for select in queries:
|
for call in calls:
|
||||||
# Selected segment can be allocated before update by someone
|
allocations = call()
|
||||||
# else
|
for alloc in allocations:
|
||||||
allocs = select.limit(IDPOOL_SELECT_SIZE).all()
|
segment = dict((k, alloc[k]) for k in self.primary_keys)
|
||||||
|
if self.segmentation_obj.allocate(context, **segment):
|
||||||
if not allocs:
|
LOG.debug('%(type)s segment allocate from pool success '
|
||||||
# No resource available
|
'with %(segment)s ', {'type': network_type,
|
||||||
continue
|
'segment': segment})
|
||||||
|
|
||||||
alloc = random.choice(allocs)
|
|
||||||
raw_segment = dict((k, alloc[k]) for k in self.primary_keys)
|
|
||||||
LOG.debug("%(type)s segment allocate from pool "
|
|
||||||
"started with %(segment)s ",
|
|
||||||
{"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 allocate from pool "
|
|
||||||
"success with %(segment)s ",
|
|
||||||
{"type": network_type,
|
|
||||||
"segment": raw_segment})
|
|
||||||
return alloc
|
return alloc
|
||||||
|
|
||||||
# Segment allocated since select
|
|
||||||
LOG.debug("Allocate %(type)s segment from pool "
|
|
||||||
"failed with segment %(segment)s",
|
|
||||||
{"type": network_type,
|
|
||||||
"segment": raw_segment})
|
|
||||||
# saving real exception in case we exceeded amount of attempts
|
|
||||||
raise db_exc.RetryRequest(
|
raise db_exc.RetryRequest(
|
||||||
exceptions.NoNetworkFoundInMaximumAllowedAttempts())
|
exceptions.NoNetworkFoundInMaximumAllowedAttempts())
|
||||||
|
34
neutron/tests/unit/objects/plugins/ml2/test_base.py
Normal file
34
neutron/tests/unit/objects/plugins/ml2/test_base.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Copyright (c) 2020 Red Hat, Inc.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
class SegmentAllocationDbObjTestCase(object):
|
||||||
|
|
||||||
|
def test_get_unallocated_segments(self):
|
||||||
|
self.assertEqual(
|
||||||
|
[], self._test_class.get_unallocated_segments(self.context))
|
||||||
|
|
||||||
|
obj = self.objs[0]
|
||||||
|
obj.allocated = True
|
||||||
|
obj.create()
|
||||||
|
self.assertEqual(
|
||||||
|
[], self._test_class.get_unallocated_segments(self.context))
|
||||||
|
|
||||||
|
obj = self.objs[1]
|
||||||
|
obj.allocated = False
|
||||||
|
obj.create()
|
||||||
|
allocations = self._test_class.get_unallocated_segments(self.context)
|
||||||
|
self.assertEqual(1, len(allocations))
|
||||||
|
self.assertEqual(obj.segmentation_id, allocations[0].segmentation_id)
|
@ -13,6 +13,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from neutron.objects.plugins.ml2 import geneveallocation
|
from neutron.objects.plugins.ml2 import geneveallocation
|
||||||
|
from neutron.tests.unit.objects.plugins.ml2 import test_base as ml2_test_base
|
||||||
from neutron.tests.unit.objects import test_base
|
from neutron.tests.unit.objects import test_base
|
||||||
from neutron.tests.unit import testlib_api
|
from neutron.tests.unit import testlib_api
|
||||||
|
|
||||||
@ -22,8 +23,9 @@ class GeneveAllocationIfaceObjTestCase(test_base.BaseObjectIfaceTestCase):
|
|||||||
_test_class = geneveallocation.GeneveAllocation
|
_test_class = geneveallocation.GeneveAllocation
|
||||||
|
|
||||||
|
|
||||||
class GeneveAllocationDbObjTestCase(test_base.BaseDbObjectTestCase,
|
class GeneveAllocationDbObjTestCase(
|
||||||
testlib_api.SqlTestCase):
|
test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase,
|
||||||
|
ml2_test_base.SegmentAllocationDbObjTestCase):
|
||||||
|
|
||||||
_test_class = geneveallocation.GeneveAllocation
|
_test_class = geneveallocation.GeneveAllocation
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from neutron.objects.plugins.ml2 import greallocation as gre_object
|
from neutron.objects.plugins.ml2 import greallocation as gre_object
|
||||||
|
from neutron.tests.unit.objects.plugins.ml2 import test_base as ml2_test_base
|
||||||
from neutron.tests.unit.objects import test_base
|
from neutron.tests.unit.objects import test_base
|
||||||
from neutron.tests.unit import testlib_api
|
from neutron.tests.unit import testlib_api
|
||||||
|
|
||||||
@ -22,8 +23,9 @@ class GreAllocationIfaceObjTestCase(test_base.BaseObjectIfaceTestCase):
|
|||||||
_test_class = gre_object.GreAllocation
|
_test_class = gre_object.GreAllocation
|
||||||
|
|
||||||
|
|
||||||
class GreAllocationDbObjTestCase(test_base.BaseDbObjectTestCase,
|
class GreAllocationDbObjTestCase(
|
||||||
testlib_api.SqlTestCase):
|
test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase,
|
||||||
|
ml2_test_base.SegmentAllocationDbObjTestCase):
|
||||||
|
|
||||||
_test_class = gre_object.GreAllocation
|
_test_class = gre_object.GreAllocation
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from neutron.objects.plugins.ml2 import vlanallocation
|
from neutron.objects.plugins.ml2 import vlanallocation
|
||||||
|
from neutron.tests.unit.objects.plugins.ml2 import test_base as ml2_test_base
|
||||||
from neutron.tests.unit.objects import test_base
|
from neutron.tests.unit.objects import test_base
|
||||||
from neutron.tests.unit import testlib_api
|
from neutron.tests.unit import testlib_api
|
||||||
|
|
||||||
@ -22,7 +23,8 @@ class VlanAllocationIfaceObjTestCase(test_base.BaseObjectIfaceTestCase):
|
|||||||
_test_class = vlanallocation.VlanAllocation
|
_test_class = vlanallocation.VlanAllocation
|
||||||
|
|
||||||
|
|
||||||
class VlanAllocationDbObjTestCase(test_base.BaseDbObjectTestCase,
|
class VlanAllocationDbObjTestCase(
|
||||||
testlib_api.SqlTestCase):
|
test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase,
|
||||||
|
ml2_test_base.SegmentAllocationDbObjTestCase):
|
||||||
|
|
||||||
_test_class = vlanallocation.VlanAllocation
|
_test_class = vlanallocation.VlanAllocation
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from neutron.objects.plugins.ml2 import vxlanallocation as vxlan_obj
|
from neutron.objects.plugins.ml2 import vxlanallocation as vxlan_obj
|
||||||
|
from neutron.tests.unit.objects.plugins.ml2 import test_base as ml2_test_base
|
||||||
from neutron.tests.unit.objects import test_base
|
from neutron.tests.unit.objects import test_base
|
||||||
from neutron.tests.unit import testlib_api
|
from neutron.tests.unit import testlib_api
|
||||||
|
|
||||||
@ -22,8 +23,9 @@ class VxlanAllocationIfaceObjTestCase(test_base.BaseObjectIfaceTestCase):
|
|||||||
_test_class = vxlan_obj.VxlanAllocation
|
_test_class = vxlan_obj.VxlanAllocation
|
||||||
|
|
||||||
|
|
||||||
class VxlanAllocationDbObjTestCase(test_base.BaseDbObjectTestCase,
|
class VxlanAllocationDbObjTestCase(
|
||||||
testlib_api.SqlTestCase):
|
test_base.BaseDbObjectTestCase, testlib_api.SqlTestCase,
|
||||||
|
ml2_test_base.SegmentAllocationDbObjTestCase):
|
||||||
|
|
||||||
_test_class = vxlan_obj.VxlanAllocation
|
_test_class = vxlan_obj.VxlanAllocation
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import itertools
|
||||||
import random
|
import random
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
@ -22,12 +23,14 @@ from oslo_utils import uuidutils
|
|||||||
|
|
||||||
from neutron.objects import network as net_obj
|
from neutron.objects import network as net_obj
|
||||||
from neutron.objects import network_segment_range
|
from neutron.objects import network_segment_range
|
||||||
|
from neutron.objects.plugins.ml2 import base as ml2_base
|
||||||
from neutron.objects.plugins.ml2 import vlanallocation as vlan_alloc_obj
|
from neutron.objects.plugins.ml2 import vlanallocation as vlan_alloc_obj
|
||||||
from neutron.tests.unit.objects import test_base as obj_test_base
|
from neutron.tests.unit.objects import test_base as obj_test_base
|
||||||
from neutron.tests.unit import testlib_api
|
from neutron.tests.unit import testlib_api
|
||||||
|
|
||||||
TEST_TENANT_ID = '46f70361-ba71-4bd0-9769-3573fd227c4b'
|
TEST_TENANT_ID = '46f70361-ba71-4bd0-9769-3573fd227c4b'
|
||||||
TEST_PHYSICAL_NETWORK = 'phys_net'
|
TEST_PHYSICAL_NETWORK = 'phys_net'
|
||||||
|
NUM_ALLOCATIONS = 3
|
||||||
|
|
||||||
|
|
||||||
class NetworkSegmentRangeIfaceObjectTestCase(
|
class NetworkSegmentRangeIfaceObjectTestCase(
|
||||||
@ -69,16 +72,16 @@ class NetworkSegmentRangeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
|
|||||||
|
|
||||||
_test_class = network_segment_range.NetworkSegmentRange
|
_test_class = network_segment_range.NetworkSegmentRange
|
||||||
|
|
||||||
def _create_test_vlan_allocation(self, vlan_id=None, allocated=False):
|
def _create_allocation(self, allocation_class, segmentation_id=None,
|
||||||
attr = self.get_random_object_fields(vlan_alloc_obj.VlanAllocation)
|
physical_network=None, allocated=False):
|
||||||
attr.update({
|
attr = self.get_random_object_fields(allocation_class)
|
||||||
'vlan_id': vlan_id or random.randint(
|
attr['allocated'] = allocated
|
||||||
constants.MIN_VLAN_TAG, constants.MAX_VLAN_TAG),
|
allocation_class.update_primary_keys(
|
||||||
'physical_network': 'foo',
|
attr, segmentation_id=segmentation_id,
|
||||||
'allocated': allocated})
|
physical_network=physical_network or 'foo')
|
||||||
_vlan_allocation = vlan_alloc_obj.VlanAllocation(self.context, **attr)
|
allocation = allocation_class(self.context, **attr)
|
||||||
_vlan_allocation.create()
|
allocation.create()
|
||||||
return _vlan_allocation
|
return allocation
|
||||||
|
|
||||||
def _create_test_network(self, name=None, network_id=None):
|
def _create_test_network(self, name=None, network_id=None):
|
||||||
name = "test-network-%s" % helpers.get_random_string(4)
|
name = "test-network-%s" % helpers.get_random_string(4)
|
||||||
@ -89,24 +92,30 @@ class NetworkSegmentRangeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
|
|||||||
_network.create()
|
_network.create()
|
||||||
return _network
|
return _network
|
||||||
|
|
||||||
def _create_test_vlan_segment(self, segmentation_id=None, network_id=None):
|
def _create_segment(self, segmentation_id=None, network_id=None,
|
||||||
|
physical_network=None, network_type=None):
|
||||||
attr = self.get_random_object_fields(net_obj.NetworkSegment)
|
attr = self.get_random_object_fields(net_obj.NetworkSegment)
|
||||||
attr.update({
|
attr.update({
|
||||||
'network_id': network_id or self._create_test_network_id(),
|
'network_id': network_id or self._create_test_network_id(),
|
||||||
'network_type': constants.TYPE_VLAN,
|
'network_type': network_type or constants.TYPE_VLAN,
|
||||||
'physical_network': 'foo',
|
'physical_network': physical_network or 'foo',
|
||||||
'segmentation_id': segmentation_id or random.randint(
|
'segmentation_id': segmentation_id or random.randint(
|
||||||
constants.MIN_VLAN_TAG, constants.MAX_VLAN_TAG)})
|
constants.MIN_VLAN_TAG, constants.MAX_VLAN_TAG)})
|
||||||
_segment = net_obj.NetworkSegment(self.context, **attr)
|
_segment = net_obj.NetworkSegment(self.context, **attr)
|
||||||
_segment.create()
|
_segment.create()
|
||||||
return _segment
|
return _segment
|
||||||
|
|
||||||
def _create_test_vlan_network_segment_range_obj(self, minimum, maximum):
|
def _create_network_segment_range(
|
||||||
|
self, minimum, maximum, network_type=None, physical_network=None,
|
||||||
|
project_id=None, default=False):
|
||||||
kwargs = self.get_random_db_fields()
|
kwargs = self.get_random_db_fields()
|
||||||
kwargs.update({'network_type': constants.TYPE_VLAN,
|
kwargs.update({'network_type': network_type or constants.TYPE_VLAN,
|
||||||
'physical_network': 'foo',
|
'physical_network': physical_network or 'foo',
|
||||||
'minimum': minimum,
|
'minimum': minimum,
|
||||||
'maximum': maximum})
|
'maximum': maximum,
|
||||||
|
'default': default,
|
||||||
|
'shared': default,
|
||||||
|
'project_id': project_id})
|
||||||
db_obj = self._test_class.db_model(**kwargs)
|
db_obj = self._test_class.db_model(**kwargs)
|
||||||
obj_fields = self._test_class.modify_fields_from_db(db_obj)
|
obj_fields = self._test_class.modify_fields_from_db(db_obj)
|
||||||
obj = self._test_class(self.context, **obj_fields)
|
obj = self._test_class(self.context, **obj_fields)
|
||||||
@ -118,11 +127,14 @@ class NetworkSegmentRangeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
|
|||||||
to_alloc = range(range_minimum, range_maximum - 5)
|
to_alloc = range(range_minimum, range_maximum - 5)
|
||||||
not_to_alloc = range(range_maximum - 5, range_maximum + 1)
|
not_to_alloc = range(range_maximum - 5, range_maximum + 1)
|
||||||
for vlan_id in to_alloc:
|
for vlan_id in to_alloc:
|
||||||
self._create_test_vlan_allocation(vlan_id=vlan_id, allocated=True)
|
self._create_allocation(vlan_alloc_obj.VlanAllocation,
|
||||||
|
segmentation_id=vlan_id, allocated=True,
|
||||||
|
physical_network='foo')
|
||||||
for vlan_id in not_to_alloc:
|
for vlan_id in not_to_alloc:
|
||||||
self._create_test_vlan_allocation(vlan_id=vlan_id, allocated=False)
|
self._create_allocation(vlan_alloc_obj.VlanAllocation,
|
||||||
obj = self._create_test_vlan_network_segment_range_obj(range_minimum,
|
segmentation_id=vlan_id, allocated=False,
|
||||||
range_maximum)
|
physical_network='foo')
|
||||||
|
obj = self._create_network_segment_range(range_minimum, range_maximum)
|
||||||
available_alloc = self._test_class._get_available_allocation(obj)
|
available_alloc = self._test_class._get_available_allocation(obj)
|
||||||
self.assertItemsEqual(not_to_alloc, available_alloc)
|
self.assertItemsEqual(not_to_alloc, available_alloc)
|
||||||
|
|
||||||
@ -130,10 +142,10 @@ class NetworkSegmentRangeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
|
|||||||
alloc_mapping = {}
|
alloc_mapping = {}
|
||||||
for _ in range(5):
|
for _ in range(5):
|
||||||
network = self._create_test_network()
|
network = self._create_test_network()
|
||||||
segment = self._create_test_vlan_segment(network_id=network.id)
|
segment = self._create_segment(network_id=network.id)
|
||||||
alloc_mapping.update({segment.segmentation_id: network.project_id})
|
alloc_mapping.update({segment.segmentation_id: network.project_id})
|
||||||
|
|
||||||
obj = self._create_test_vlan_network_segment_range_obj(
|
obj = self._create_network_segment_range(
|
||||||
minimum=min(list(alloc_mapping.keys())),
|
minimum=min(list(alloc_mapping.keys())),
|
||||||
maximum=max(list(alloc_mapping.keys())))
|
maximum=max(list(alloc_mapping.keys())))
|
||||||
ret_alloc_mapping = self._test_class._get_used_allocation_mapping(obj)
|
ret_alloc_mapping = self._test_class._get_used_allocation_mapping(obj)
|
||||||
@ -167,3 +179,99 @@ class NetworkSegmentRangeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
|
|||||||
obj.create()
|
obj.create()
|
||||||
obj.shared = False
|
obj.shared = False
|
||||||
self.assertRaises(n_exc.ObjectActionError, obj.update)
|
self.assertRaises(n_exc.ObjectActionError, obj.update)
|
||||||
|
|
||||||
|
def _create_environment(self):
|
||||||
|
self.projects = [uuidutils.generate_uuid() for _ in range(3)]
|
||||||
|
self.segment_ranges = {
|
||||||
|
'default': [100, 120], self.projects[0]: [90, 105],
|
||||||
|
self.projects[1]: [109, 114], self.projects[2]: [117, 130]}
|
||||||
|
self.seg_min = self.segment_ranges['default'][0]
|
||||||
|
self.seg_max = self.segment_ranges['default'][1]
|
||||||
|
|
||||||
|
for subclass in ml2_base.SegmentAllocation.__subclasses__():
|
||||||
|
# Build segment ranges: default one and project specific ones.
|
||||||
|
for name, ranges in self.segment_ranges.items():
|
||||||
|
default = True if name == 'default' else False
|
||||||
|
project = name if not default else None
|
||||||
|
self._create_network_segment_range(
|
||||||
|
ranges[0], ranges[1], network_type=subclass.network_type,
|
||||||
|
project_id=project, default=default).create()
|
||||||
|
|
||||||
|
# Build allocations (non allocated).
|
||||||
|
for segmentation_id in range(self.seg_min, self.seg_max + 1):
|
||||||
|
self._create_allocation(subclass,
|
||||||
|
segmentation_id=segmentation_id)
|
||||||
|
|
||||||
|
def _default_range_set(self, project_id=None):
|
||||||
|
range_set = set(range(self.segment_ranges['default'][0],
|
||||||
|
self.segment_ranges['default'][1] + 1))
|
||||||
|
for p_id, ranges in ((p, r) for (p, r) in self.segment_ranges.items()
|
||||||
|
if p not in [project_id, 'default']):
|
||||||
|
pranges = self.segment_ranges.get(p_id, [0, 0])
|
||||||
|
prange_set = set(range(pranges[0], pranges[1] + 1))
|
||||||
|
range_set.difference_update(prange_set)
|
||||||
|
return range_set
|
||||||
|
|
||||||
|
def _allocate_random_allocations(self, allocations, subclass):
|
||||||
|
pk_cols = subclass.db_model.__table__.primary_key.columns
|
||||||
|
primary_keys = [col.name for col in pk_cols]
|
||||||
|
allocated = []
|
||||||
|
for allocation in random.sample(allocations, k=NUM_ALLOCATIONS):
|
||||||
|
segment = dict((k, allocation[k]) for k in primary_keys)
|
||||||
|
allocated.append(segment)
|
||||||
|
self.assertEqual(1, subclass.allocate(self.context, **segment))
|
||||||
|
return allocated
|
||||||
|
|
||||||
|
def test_get_segments_for_project(self):
|
||||||
|
self._create_environment()
|
||||||
|
for project_id, subclass in itertools.product(
|
||||||
|
self.projects, ml2_base.SegmentAllocation.__subclasses__()):
|
||||||
|
allocations = network_segment_range.NetworkSegmentRange. \
|
||||||
|
get_segments_for_project(
|
||||||
|
self.context, subclass.db_model, subclass.network_type,
|
||||||
|
subclass.get_segmentation_id(), project_id=project_id)
|
||||||
|
project_min = max(self.seg_min, self.segment_ranges[project_id][0])
|
||||||
|
project_max = min(self.seg_max, self.segment_ranges[project_id][1])
|
||||||
|
project_segment_ids = list(range(project_min, project_max + 1))
|
||||||
|
self.assertEqual(len(allocations), len(project_segment_ids))
|
||||||
|
for allocation in allocations:
|
||||||
|
self.assertFalse(allocation.allocated)
|
||||||
|
self.assertIn(allocation.segmentation_id, project_segment_ids)
|
||||||
|
|
||||||
|
# Allocate random segments inside the project range.
|
||||||
|
self._allocate_random_allocations(allocations, subclass)
|
||||||
|
allocations = network_segment_range.NetworkSegmentRange. \
|
||||||
|
get_segments_for_project(
|
||||||
|
self.context, subclass.db_model, subclass.network_type,
|
||||||
|
subclass.get_segmentation_id(), project_id=project_id)
|
||||||
|
self.assertEqual(len(allocations),
|
||||||
|
len(project_segment_ids) - NUM_ALLOCATIONS)
|
||||||
|
|
||||||
|
def test_get_segments_shared(self):
|
||||||
|
self._create_environment()
|
||||||
|
self.projects.append(None)
|
||||||
|
for project_id, subclass in itertools.product(
|
||||||
|
self.projects, ml2_base.SegmentAllocation.__subclasses__()):
|
||||||
|
filters = {'project_id': project_id,
|
||||||
|
'physical_network': 'foo'}
|
||||||
|
allocations = network_segment_range.NetworkSegmentRange. \
|
||||||
|
get_segments_shared(
|
||||||
|
self.context, subclass.db_model, subclass.network_type,
|
||||||
|
subclass.get_segmentation_id(), **filters)
|
||||||
|
|
||||||
|
prange = self._default_range_set(project_id)
|
||||||
|
self.assertEqual(len(prange), len(allocations))
|
||||||
|
|
||||||
|
# Allocate random segments inside the project shared range.
|
||||||
|
allocated = self._allocate_random_allocations(allocations,
|
||||||
|
subclass)
|
||||||
|
allocations = network_segment_range.NetworkSegmentRange. \
|
||||||
|
get_segments_shared(
|
||||||
|
self.context, subclass.db_model, subclass.network_type,
|
||||||
|
subclass.get_segmentation_id(), **filters)
|
||||||
|
self.assertEqual(len(allocations), len(prange) - NUM_ALLOCATIONS)
|
||||||
|
|
||||||
|
# Deallocate the allocated segments because can be allocated in
|
||||||
|
# a segmentation ID not belonging to any project.
|
||||||
|
for alloc in allocated:
|
||||||
|
self.assertEqual(1, subclass.deallocate(self.context, **alloc))
|
||||||
|
Loading…
Reference in New Issue
Block a user