neutron/neutron/tests/unit/extensions/test_subnet_onboard.py

254 lines
12 KiB
Python

# (c) Copyright 2019 SUSE LLC
#
# 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 contextlib
import netaddr
from neutron_lib.db import api as db_api
from neutron_lib import exceptions as exc
from oslo_utils import uuidutils
from neutron.objects import subnet as subnet_obj
from neutron.objects import subnetpool as subnetpool_obj
from neutron.tests.unit.plugins.ml2 import test_plugin
_uuid = uuidutils.generate_uuid
class SubnetOnboardTestsBase(object):
@contextlib.contextmanager
def address_scope(self, ip_version, prefixes=None, shared=False,
admin=True, name='test-scope', is_default_pool=False,
tenant_id=None, **kwargs):
if not tenant_id:
tenant_id = _uuid()
scope_data = {'tenant_id': tenant_id, 'ip_version': ip_version,
'shared': shared, 'name': name + '-scope'}
with db_api.CONTEXT_WRITER.using(self.context):
yield self.driver.create_address_scope(
self.context,
{'address_scope': scope_data})
@contextlib.contextmanager
def subnetpool(self, ip_version, prefixes=None, shared=False, admin=True,
name='test-pool', is_default_pool=False, tenant_id=None,
address_scope_id=None, **kwargs):
if not tenant_id:
tenant_id = _uuid()
pool_data = {'tenant_id': tenant_id, 'shared': shared, 'name': name,
'address_scope_id': address_scope_id,
'prefixes': prefixes, 'is_default': is_default_pool}
for key in kwargs:
pool_data[key] = kwargs[key]
with db_api.CONTEXT_WRITER.using(self.context):
yield self.driver.create_subnetpool(self.context,
{'subnetpool': pool_data})
def test_onboard_subnet_no_address_scope(self):
with self.subnetpool(self.ip_version,
prefixes=self.subnetpool_prefixes) as subnetpool:
self._test_onboard_cidr(subnetpool['id'], self.cidr_to_onboard)
def test_onboard_subnet_address_scope(self):
with self.address_scope(self.ip_version) as addr_scope:
with self.subnetpool(self.ip_version,
prefixes=self.subnetpool_prefixes,
address_scope_id=addr_scope['id']) as subnetpool:
self._test_onboard_cidr(subnetpool['id'], self.cidr_to_onboard)
def test_onboard_subnet_overlapping_cidr_no_address_scope(self):
with self.subnetpool(self.ip_version,
prefixes=self.subnetpool_prefixes) as subnetpool:
with self.subnet(cidr=self.overlapping_cidr,
subnetpool_id=subnetpool['id'],
ip_version=self.ip_version):
self.assertRaises(exc.IllegalSubnetPoolUpdate,
self._test_onboard_cidr,
subnetpool['id'],
self.overlapping_cidr)
def test_onboard_subnet_address_scope_multiple_pools(self):
with self.address_scope(self.ip_version) as addr_scope:
with self.subnetpool(self.ip_version,
prefixes=[self.subnetpool_prefixes[0]],
address_scope_id=addr_scope['id']) as onboard_pool,\
self.subnetpool(self.ip_version,
prefixes=[self.subnetpool_prefixes[1]],
address_scope_id=addr_scope['id']):
self._test_onboard_cidr(onboard_pool['id'],
self.cidr_to_onboard)
def test_onboard_subnet_address_scope_overlap_multiple_pools(self):
with self.address_scope(self.ip_version) as addr_scope:
with self.subnetpool(self.ip_version,
prefixes=[self.subnetpool_prefixes[0]],
address_scope_id=addr_scope['id']) as onboard_pool,\
self.subnetpool(self.ip_version,
prefixes=[self.subnetpool_prefixes[1]],
address_scope_id=addr_scope['id']) as other_pool:
self.assertRaises(exc.AddressScopePrefixConflict,
self._test_onboard_cidr,
onboard_pool['id'],
other_pool['prefixes'][0])
def test_onboard_subnet_move_between_pools_same_address_scope(self):
with self.address_scope(self.ip_version) as addr_scope:
with self.subnetpool(self.ip_version,
prefixes=[self.cidr_to_onboard],
address_scope_id=addr_scope['id']) as source:
with self.subnetpool(self.ip_version,
address_scope_id=addr_scope['id'],
prefixes=self.subnetpool_prefixes) as target:
with self.subnet(cidr=self.cidr_to_onboard,
ip_version=self.ip_version) as subnet_to_onboard:
subnet_to_onboard = subnet_to_onboard['subnet']
# Onboard subnet into an initial subnet pool
self._test_onboard_network_subnets(
subnet_to_onboard['network_id'], source['id'])
source_pool_subnets = subnet_obj.Subnet.get_objects(
self.context,
subnetpool_id=source['id'])
self.assertEqual(1, len(source_pool_subnets))
# Attempt to move the subnet to the target pool
self.assertRaises(exc.AddressScopePrefixConflict,
self._test_onboard_network_subnets,
subnet_to_onboard['network_id'], target['id'])
def test_onboard_subnet_move_between_pools(self):
with self.subnetpool(self.ip_version,
prefixes=self.subnetpool_prefixes) as source:
with self.subnetpool(self.ip_version,
prefixes=self.subnetpool_prefixes) as target:
with self.subnet(cidr=self.cidr_to_onboard,
ip_version=self.ip_version) as subnet_to_onboard:
subnet_to_onboard = subnet_to_onboard['subnet']
# Onboard subnet into an initial subnet pool
self._test_onboard_network_subnets(
subnet_to_onboard['network_id'], source['id'])
source_pool_subnets = subnet_obj.Subnet.get_objects(
self.context,
subnetpool_id=source['id'])
self.assertEqual(1, len(source_pool_subnets))
# Attempt to onboard subnet into a different pool
self._test_onboard_network_subnets(
subnet_to_onboard['network_id'], target['id'])
source_pool_subnets = subnet_obj.Subnet.get_objects(
self.context,
subnetpool_id=source['id'])
target_pool_subnets = subnet_obj.Subnet.get_objects(
self.context,
subnetpool_id=target['id'])
source_subnetpool = subnetpool_obj.SubnetPool.get_object(
self.context,
id=source['id'])
# Assert that the subnet prefix has not been removed
# from the the source prefix list. The prefix should
# simply be released back to the pool, not removed.
self.assertIn(
netaddr.IPNetwork(self.cidr_to_onboard),
netaddr.IPSet(source_subnetpool['prefixes']))
# Assert the subnet is associated with the proper pool
self.assertEqual(0, len(source_pool_subnets))
self.assertEqual(1, len(target_pool_subnets))
def test_onboard_subnet_invalid_request(self):
with self.subnetpool(self.ip_version,
prefixes=self.subnetpool_prefixes) as subnetpool:
self.assertRaises(exc.InvalidInput,
self._test_onboard_subnet_no_network_id,
subnetpool['id'], self.cidr_to_onboard)
def test_onboard_subnet_network_not_found(self):
with self.subnetpool(self.ip_version,
prefixes=self.subnetpool_prefixes) as subnetpool:
self.assertRaises(exc.NetworkNotFound,
self._test_onboard_subnet_non_existing_network,
subnetpool['id'], self.cidr_to_onboard)
def _test_onboard_subnet_no_network_id(self, subnetpool_id,
cidr_to_onboard):
with self.subnet(cidr=cidr_to_onboard,
ip_version=self.ip_version) as subnet_to_onboard:
subnet_to_onboard = subnet_to_onboard['subnet']
self.driver.onboard_network_subnets(
self.context, subnetpool_id, {})
def _test_onboard_subnet_non_existing_network(self, subnetpool_id,
cidr_to_onboard):
with self.subnet(cidr=cidr_to_onboard,
ip_version=self.ip_version) as subnet_to_onboard:
subnet_to_onboard = subnet_to_onboard['subnet']
self.driver.onboard_network_subnets(
self.context, subnetpool_id,
{'network_id': _uuid()})
def _test_onboard_network_subnets(self, network_id, subnetpool_id):
response = self.driver.onboard_network_subnets(
self.context,
subnetpool_id,
{'network_id': network_id})
subnetpool = subnetpool_obj.SubnetPool.get_object(self.context,
id=subnetpool_id)
subnetpool_prefixes = netaddr.IPSet(subnetpool.prefixes)
for onboarded_subnet in subnet_obj.Subnet.get_objects(
self.context,
ip_version=self.ip_version,
network_id=network_id):
onboarded_prefix = netaddr.IPNetwork(onboarded_subnet.cidr)
self.assertIn({'id': onboarded_subnet.id,
'cidr': onboarded_subnet.cidr}, response)
self.assertEqual(subnetpool_id,
onboarded_subnet.subnetpool_id)
self.assertIn(onboarded_prefix, subnetpool_prefixes)
def _test_onboard_cidr(self, subnetpool_id, cidr_to_onboard):
with self.subnet(cidr=cidr_to_onboard,
ip_version=self.ip_version) as subnet_to_onboard:
subnet_to_onboard = subnet_to_onboard['subnet']
self._test_onboard_network_subnets(
subnet_to_onboard['network_id'],
subnetpool_id)
class SubnetOnboardTestsIpv4(SubnetOnboardTestsBase,
test_plugin.Ml2PluginV2TestCase):
subnetpool_prefixes = ["192.168.1.0/24", "192.168.2.0/24"]
cidr_to_onboard = "10.0.0.0/24"
overlapping_cidr = "192.168.1.128/25"
default_prefixlen = 24
ip_version = 4
class SubnetOnboardTestsIpv6(SubnetOnboardTestsBase,
test_plugin.Ml2PluginV2TestCase):
subnetpool_prefixes = ["2001:db8:1234::/48",
"2001:db8:1235::/48"]
cidr_to_onboard = "2001:db8:4321::/48"
overlapping_cidr = "2001:db8:1234:1111::/64"
default_prefixlen = 64
ip_version = 6