# (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