Default (shared) network segment range is not mandatory

When "network_segment_range" service extension is enabled, the default
(shared) network segment range could not exist. In this case, when
retrieving the segmentation IDs, the existance of this range should be
checked first.

Conflicts:
      neutron/objects/network_segment_range.py

Change-Id: Iaff891a48adc811ab114fb03b24ab3da9311eec3
Closes-Bug: #1870569
(cherry picked from commit 2e6aa290a3)
(cherry picked from commit 222beb3a8d)
This commit is contained in:
Rodolfo Alonso Hernandez 2020-04-06 13:19:57 +00:00
parent 3796c03fd1
commit 8adac1ac3e
2 changed files with 66 additions and 13 deletions

View File

@ -188,15 +188,19 @@ class NetworkSegmentRange(base.NeutronDbObject):
_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(
# Retrieve all network segment ranges shared.
shared_ranges = context.session.query(cls.db_model).filter(
and_(cls.db_model.network_type == network_type,
cls.db_model.default == sql.expression.true()))
cls.db_model.shared == sql.expression.true()))
if network_type == constants.TYPE_VLAN:
default_range.filter(cls.db_model.physical_network ==
shared_ranges.filter(cls.db_model.physical_network ==
_filters['physical_network'])
segment_ids = set(six_range(default_range.all()[0].minimum,
default_range.all()[0].maximum + 1))
segment_ids = set([])
for shared_range in shared_ranges.all():
segment_ids.update(set(six_range(shared_range.minimum,
shared_range.maximum + 1)))
if not segment_ids:
return []
# Retrieve other project segment ID ranges (not own project, not
# default range).
@ -226,8 +230,8 @@ class NetworkSegmentRange(base.NeutronDbObject):
# 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]
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()

View File

@ -107,14 +107,14 @@ class NetworkSegmentRangeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
def _create_network_segment_range(
self, minimum, maximum, network_type=None, physical_network=None,
project_id=None, default=False):
project_id=None, default=False, shared=False):
kwargs = self.get_random_db_fields()
kwargs.update({'network_type': network_type or constants.TYPE_VLAN,
'physical_network': physical_network or 'foo',
'minimum': minimum,
'maximum': maximum,
'default': default,
'shared': default,
'shared': shared,
'project_id': project_id})
db_obj = self._test_class.db_model(**kwargs)
obj_fields = self._test_class.modify_fields_from_db(db_obj)
@ -180,7 +180,7 @@ class NetworkSegmentRangeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
obj.shared = False
self.assertRaises(n_exc.ObjectActionError, obj.update)
def _create_environment(self):
def _create_environment(self, default_range=True):
self.projects = [uuidutils.generate_uuid() for _ in range(3)]
self.segment_ranges = {
'default': [100, 120], self.projects[0]: [90, 105],
@ -193,15 +193,31 @@ class NetworkSegmentRangeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
for name, ranges in self.segment_ranges.items():
default = True if name == 'default' else False
project = name if not default else None
if default and not default_range:
continue
self._create_network_segment_range(
ranges[0], ranges[1], network_type=subclass.network_type,
project_id=project, default=default).create()
project_id=project, default=default,
shared=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 _create_shared_ranges(self):
self.shared_ranges = {0: [100, 105], 1: [110, 115]}
self.shared_ids = set(itertools.chain.from_iterable(
list(range(r[0], r[1] + 1)) for r in self.shared_ranges.values()))
for shared_range, subclass in itertools.product(
self.shared_ranges.values(),
ml2_base.SegmentAllocation.__subclasses__()):
self._create_network_segment_range(
shared_range[0], shared_range[1],
network_type=subclass.network_type, default=False,
shared=True).create()
def _default_range_set(self, project_id=None):
range_set = set(range(self.segment_ranges['default'][0],
self.segment_ranges['default'][1] + 1))
@ -275,3 +291,36 @@ class NetworkSegmentRangeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
# a segmentation ID not belonging to any project.
for alloc in allocated:
self.assertEqual(1, subclass.deallocate(self.context, **alloc))
def test_get_segments_shared_no_shared_ranges(self):
self._create_environment(default_range=False)
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)
self.assertEqual([], allocations)
def test_get_segments_shared_no_default_range_two_shared_ranges(self):
self._create_environment(default_range=False)
self.projects.append(None)
self._create_shared_ranges()
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)
available_ids = prange & self.shared_ids
self.assertEqual(len(available_ids), len(allocations))
for alloc in allocations:
self.assertIn(alloc.segmentation_id, available_ids)