7594bb0627
Now that we are python3 only, we should move to using the built in version of mock that supports all of our testing needs and remove the dependency on the "mock" package. This patch moves all references to "import mock" to "from unittest import mock". It also cleans up some new line inconsistency. Fixed an inconsistency in the OVSBridge.deferred() definition as it needs to also have an *args argument. Fixed an issue where an l3-agent test was mocking functools.partial, causing a python3.8 failure. Unit tests only, removing from tests/base.py affects functional tests which need additional work. Change-Id: I40e8a8410840c3774c72ae1a8054574445d66ece
327 lines
15 KiB
Python
327 lines
15 KiB
Python
# Copyright (c) 2019 Intel Corporation.
|
|
#
|
|
# 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 itertools
|
|
import random
|
|
from unittest import mock
|
|
|
|
from neutron_lib import constants
|
|
from neutron_lib import exceptions as n_exc
|
|
from neutron_lib.utils import helpers
|
|
from oslo_utils import uuidutils
|
|
|
|
from neutron.objects import network as net_obj
|
|
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.tests.unit.objects import test_base as obj_test_base
|
|
from neutron.tests.unit import testlib_api
|
|
|
|
TEST_TENANT_ID = '46f70361-ba71-4bd0-9769-3573fd227c4b'
|
|
TEST_PHYSICAL_NETWORK = 'phys_net'
|
|
NUM_ALLOCATIONS = 3
|
|
|
|
|
|
class NetworkSegmentRangeIfaceObjectTestCase(
|
|
obj_test_base.BaseObjectIfaceTestCase):
|
|
|
|
_test_class = network_segment_range.NetworkSegmentRange
|
|
|
|
def setUp(self):
|
|
self._mock_get_available_allocation = mock.patch.object(
|
|
network_segment_range.NetworkSegmentRange,
|
|
'_get_available_allocation',
|
|
return_value=[])
|
|
self.mock_get_available_allocation = (
|
|
self._mock_get_available_allocation.start())
|
|
self._mock_get_used_allocation_mapping = mock.patch.object(
|
|
network_segment_range.NetworkSegmentRange,
|
|
'_get_used_allocation_mapping',
|
|
return_value={})
|
|
self.mock_get_used_allocation_mapping = (
|
|
self._mock_get_used_allocation_mapping.start())
|
|
super(NetworkSegmentRangeIfaceObjectTestCase, self).setUp()
|
|
# `project_id` and `physical_network` attributes in
|
|
# network_segment_range are nullable, depending on the value of
|
|
# `shared` and `network_type` respectively.
|
|
# Hack to always populate test project_id and physical_network
|
|
# fields in network segment range Iface object testing so that related
|
|
# tests like `test_create_updates_from_db_object` and
|
|
# `test_update_updates_from_db_object` can have those fields.
|
|
# Alternatives can be skipping those tests when executing
|
|
# NetworkSegmentRangeIfaceObjectTestCase, or making base test case
|
|
# adjustments.
|
|
self.update_obj_fields({'project_id': TEST_TENANT_ID,
|
|
'physical_network': TEST_PHYSICAL_NETWORK})
|
|
self.extra_fields_not_in_dict = ['tenant_id']
|
|
|
|
|
|
class NetworkSegmentRangeDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
|
|
testlib_api.SqlTestCase):
|
|
|
|
_test_class = network_segment_range.NetworkSegmentRange
|
|
|
|
def _create_allocation(self, allocation_class, segmentation_id=None,
|
|
physical_network=None, allocated=False):
|
|
attr = self.get_random_object_fields(allocation_class)
|
|
attr['allocated'] = allocated
|
|
allocation_class.update_primary_keys(
|
|
attr, segmentation_id=segmentation_id,
|
|
physical_network=physical_network or 'foo')
|
|
allocation = allocation_class(self.context, **attr)
|
|
allocation.create()
|
|
return allocation
|
|
|
|
def _create_test_network(self, name=None, network_id=None):
|
|
name = "test-network-%s" % helpers.get_random_string(4)
|
|
network_id = (uuidutils.generate_uuid() if network_id is None
|
|
else network_id)
|
|
_network = net_obj.Network(self.context, name=name, id=network_id,
|
|
project_id=uuidutils.generate_uuid())
|
|
_network.create()
|
|
return _network
|
|
|
|
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.update({
|
|
'network_id': network_id or self._create_test_network_id(),
|
|
'network_type': network_type or constants.TYPE_VLAN,
|
|
'physical_network': physical_network or 'foo',
|
|
'segmentation_id': segmentation_id or random.randint(
|
|
constants.MIN_VLAN_TAG, constants.MAX_VLAN_TAG)})
|
|
_segment = net_obj.NetworkSegment(self.context, **attr)
|
|
_segment.create()
|
|
return _segment
|
|
|
|
def _create_network_segment_range(
|
|
self, minimum, maximum, network_type=None, physical_network=None,
|
|
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': shared,
|
|
'project_id': project_id})
|
|
db_obj = self._test_class.db_model(**kwargs)
|
|
obj_fields = self._test_class.modify_fields_from_db(db_obj)
|
|
obj = self._test_class(self.context, **obj_fields)
|
|
return obj
|
|
|
|
def test__get_available_allocation(self):
|
|
range_minimum = 100
|
|
range_maximum = 120
|
|
to_alloc = range(range_minimum, range_maximum - 5)
|
|
not_to_alloc = range(range_maximum - 5, range_maximum + 1)
|
|
for vlan_id in to_alloc:
|
|
self._create_allocation(vlan_alloc_obj.VlanAllocation,
|
|
segmentation_id=vlan_id, allocated=True,
|
|
physical_network='foo')
|
|
for vlan_id in not_to_alloc:
|
|
self._create_allocation(vlan_alloc_obj.VlanAllocation,
|
|
segmentation_id=vlan_id, allocated=False,
|
|
physical_network='foo')
|
|
obj = self._create_network_segment_range(range_minimum, range_maximum)
|
|
available_alloc = self._test_class._get_available_allocation(obj)
|
|
self.assertItemsEqual(not_to_alloc, available_alloc)
|
|
|
|
def test__get_used_allocation_mapping(self):
|
|
alloc_mapping = {}
|
|
for _ in range(5):
|
|
network = self._create_test_network()
|
|
segment = self._create_segment(network_id=network.id)
|
|
alloc_mapping.update({segment.segmentation_id: network.project_id})
|
|
|
|
obj = self._create_network_segment_range(
|
|
minimum=min(list(alloc_mapping.keys())),
|
|
maximum=max(list(alloc_mapping.keys())))
|
|
ret_alloc_mapping = self._test_class._get_used_allocation_mapping(obj)
|
|
self.assertDictEqual(alloc_mapping, ret_alloc_mapping)
|
|
|
|
def _define_network_segment_range(self, shared=False,
|
|
remove_project_id=False):
|
|
attrs = self.get_random_object_fields(obj_cls=self._test_class)
|
|
obj = self._test_class(self.context, **attrs)
|
|
obj.shared = shared
|
|
obj.project_id = None if remove_project_id else obj.project_id
|
|
return obj
|
|
|
|
def test_create_not_shared_with_project_id(self):
|
|
obj = self._define_network_segment_range()
|
|
obj.create()
|
|
|
|
def test_create_not_shared_without_project_id(self):
|
|
obj = self._define_network_segment_range(remove_project_id=True)
|
|
self.assertRaises(n_exc.ObjectActionError, obj.create)
|
|
|
|
def test_update_not_shared_with_project_id(self):
|
|
obj = self._define_network_segment_range(shared=True)
|
|
obj.create()
|
|
obj.shared = False
|
|
obj.update()
|
|
|
|
def test_update_not_shared_without_project_id(self):
|
|
obj = self._define_network_segment_range(shared=True,
|
|
remove_project_id=True)
|
|
obj.create()
|
|
obj.shared = False
|
|
self.assertRaises(n_exc.ObjectActionError, obj.update)
|
|
|
|
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],
|
|
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
|
|
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,
|
|
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))
|
|
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))
|
|
|
|
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)
|