diff --git a/neutron/objects/network_segment_range.py b/neutron/objects/network_segment_range.py index 6614fa00839..79d57f134d5 100644 --- a/neutron/objects/network_segment_range.py +++ b/neutron/objects/network_segment_range.py @@ -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() diff --git a/neutron/tests/unit/objects/test_network_segment_range.py b/neutron/tests/unit/objects/test_network_segment_range.py index 63354c6a4b0..1e4736b8369 100644 --- a/neutron/tests/unit/objects/test_network_segment_range.py +++ b/neutron/tests/unit/objects/test_network_segment_range.py @@ -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)