Merge "Enable adoption of subnets into a subnet pool"
This commit is contained in:
commit
018595d4b0
@ -17,6 +17,7 @@ from neutron.conf.policies import base
|
|||||||
|
|
||||||
COLLECTION_PATH = '/subnetpools'
|
COLLECTION_PATH = '/subnetpools'
|
||||||
RESOURCE_PATH = '/subnetpools/{id}'
|
RESOURCE_PATH = '/subnetpools/{id}'
|
||||||
|
ONBOARD_PATH = '/subnetpools/{id}/onboard_network_subnets'
|
||||||
|
|
||||||
|
|
||||||
rules = [
|
rules = [
|
||||||
@ -106,7 +107,18 @@ rules = [
|
|||||||
'path': RESOURCE_PATH,
|
'path': RESOURCE_PATH,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
)
|
),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
'onboard_network_subnets',
|
||||||
|
base.RULE_ADMIN_OR_OWNER,
|
||||||
|
'Onboard existing subnet into a subnetpool',
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'method': 'Put',
|
||||||
|
'path': ONBOARD_PATH,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -1254,6 +1254,70 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
|
|||||||
raise exc.SubnetPoolDeleteError(reason=reason)
|
raise exc.SubnetPoolDeleteError(reason=reason)
|
||||||
subnetpool.delete()
|
subnetpool.delete()
|
||||||
|
|
||||||
|
@db_api.retry_if_session_inactive()
|
||||||
|
def onboard_network_subnets(self, context, subnetpool_id, network_info):
|
||||||
|
network_id = network_info.get('network_id')
|
||||||
|
if not validators.is_attr_set(network_id):
|
||||||
|
msg = _("network_id must be specified.")
|
||||||
|
raise exc.InvalidInput(error_message=msg)
|
||||||
|
if not network_obj.Network.objects_exist(context, id=network_id):
|
||||||
|
raise exc.NetworkNotFound(net_id=network_id)
|
||||||
|
|
||||||
|
subnetpool = subnetpool_obj.SubnetPool.get_object(context,
|
||||||
|
id=subnetpool_id)
|
||||||
|
if not subnetpool:
|
||||||
|
raise exc.SubnetPoolNotFound(subnetpool_id=id)
|
||||||
|
|
||||||
|
subnets_to_onboard = subnet_obj.Subnet.get_objects(
|
||||||
|
context,
|
||||||
|
network_id=network_id,
|
||||||
|
ip_version=subnetpool.ip_version)
|
||||||
|
|
||||||
|
self._onboard_network_subnets(context, subnets_to_onboard, subnetpool)
|
||||||
|
|
||||||
|
if subnetpool.address_scope_id:
|
||||||
|
# Notify all affected routers of any address scope changes
|
||||||
|
registry.notify(resources.SUBNETPOOL_ADDRESS_SCOPE,
|
||||||
|
events.AFTER_UPDATE,
|
||||||
|
self.onboard_network_subnets,
|
||||||
|
payload=events.DBEventPayload(
|
||||||
|
context, resource_id=subnetpool_id))
|
||||||
|
|
||||||
|
onboard_info = []
|
||||||
|
for subnet in subnets_to_onboard:
|
||||||
|
onboard_info.append({'id': subnet.id, 'cidr': subnet.cidr})
|
||||||
|
|
||||||
|
return onboard_info
|
||||||
|
|
||||||
|
def _onboard_network_subnets(self, context, subnets_to_onboard,
|
||||||
|
subnetpool):
|
||||||
|
allocated_prefix_set = netaddr.IPSet(
|
||||||
|
[x.cidr for x in subnet_obj.Subnet.get_objects(
|
||||||
|
context,
|
||||||
|
subnetpool_id=subnetpool.id)])
|
||||||
|
prefixes_to_add = []
|
||||||
|
|
||||||
|
for subnet in subnets_to_onboard:
|
||||||
|
to_onboard_ipset = netaddr.IPSet([subnet.cidr])
|
||||||
|
if to_onboard_ipset & allocated_prefix_set:
|
||||||
|
args = {'subnet_id': subnet.id,
|
||||||
|
'cidr': subnet.cidr,
|
||||||
|
'subnetpool_id': subnetpool.id}
|
||||||
|
msg = _('Onboarding subnet %(subnet_id)s: %(cidr)s conflicts '
|
||||||
|
'with allocated prefixes in subnet pool '
|
||||||
|
'%(subnetpool_id)s') % args
|
||||||
|
raise exc.IllegalSubnetPoolUpdate(reason=msg)
|
||||||
|
prefixes_to_add.append(subnet.cidr)
|
||||||
|
|
||||||
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
|
new_sp_prefixes = subnetpool.prefixes + prefixes_to_add
|
||||||
|
sp_update_req = {'subnetpool': {'prefixes': new_sp_prefixes}}
|
||||||
|
|
||||||
|
self.update_subnetpool(context, subnetpool.id, sp_update_req)
|
||||||
|
for subnet in subnets_to_onboard:
|
||||||
|
subnet.subnetpool_id = subnetpool.id
|
||||||
|
subnet.update()
|
||||||
|
|
||||||
def _check_mac_addr_update(self, context, port, new_mac, device_owner):
|
def _check_mac_addr_update(self, context, port, new_mac, device_owner):
|
||||||
if (device_owner and
|
if (device_owner and
|
||||||
device_owner.startswith(
|
device_owner.startswith(
|
||||||
|
39
neutron/extensions/subnet_onboard.py
Normal file
39
neutron/extensions/subnet_onboard.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# (c) Copyright 2017 Hewlett Packard Enterprise Development LP
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from neutron_lib.api.definitions import subnet_onboard as subnet_onboard_def
|
||||||
|
from neutron_lib.api.definitions import subnetpool as subnetpool_def
|
||||||
|
from neutron_lib.api import extensions
|
||||||
|
|
||||||
|
from neutron.api.v2 import resource_helper
|
||||||
|
|
||||||
|
|
||||||
|
class Subnet_onboard(extensions.APIExtensionDescriptor):
|
||||||
|
"""API extension for subnet onboard."""
|
||||||
|
|
||||||
|
api_definition = subnet_onboard_def
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_resources(cls):
|
||||||
|
"""Returns Ext Resources."""
|
||||||
|
plural_mappings = resource_helper.build_plural_mappings(
|
||||||
|
{}, subnetpool_def.RESOURCE_ATTRIBUTE_MAP)
|
||||||
|
return resource_helper.build_resource_info(
|
||||||
|
plural_mappings,
|
||||||
|
subnetpool_def.RESOURCE_ATTRIBUTE_MAP,
|
||||||
|
None,
|
||||||
|
action_map=subnet_onboard_def.ACTION_MAP,
|
||||||
|
register_quota=True)
|
@ -42,6 +42,7 @@ from neutron_lib.api.definitions import portbindings_extended as pbe_ext
|
|||||||
from neutron_lib.api.definitions import provider_net
|
from neutron_lib.api.definitions import provider_net
|
||||||
from neutron_lib.api.definitions import security_groups_port_filtering
|
from neutron_lib.api.definitions import security_groups_port_filtering
|
||||||
from neutron_lib.api.definitions import subnet as subnet_def
|
from neutron_lib.api.definitions import subnet as subnet_def
|
||||||
|
from neutron_lib.api.definitions import subnet_onboard as subnet_onboard_def
|
||||||
from neutron_lib.api.definitions import vlantransparent as vlan_apidef
|
from neutron_lib.api.definitions import vlantransparent as vlan_apidef
|
||||||
from neutron_lib.api import extensions
|
from neutron_lib.api import extensions
|
||||||
from neutron_lib.api import validators
|
from neutron_lib.api import validators
|
||||||
@ -193,7 +194,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
filter_apidef.ALIAS,
|
filter_apidef.ALIAS,
|
||||||
port_mac_address_regenerate.ALIAS,
|
port_mac_address_regenerate.ALIAS,
|
||||||
pbe_ext.ALIAS,
|
pbe_ext.ALIAS,
|
||||||
agent_resources_synced.ALIAS]
|
agent_resources_synced.ALIAS,
|
||||||
|
subnet_onboard_def.ALIAS]
|
||||||
|
|
||||||
# List of agent types for which all binding_failed ports should try to be
|
# List of agent types for which all binding_failed ports should try to be
|
||||||
# rebound when agent revive
|
# rebound when agent revive
|
||||||
|
256
neutron/tests/unit/extensions/test_subnet_onboard.py
Normal file
256
neutron/tests/unit/extensions/test_subnet_onboard.py
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
# (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
|
13
releasenotes/notes/subnet-onboard-e4d09fa403a1053e.yaml
Normal file
13
releasenotes/notes/subnet-onboard-e4d09fa403a1053e.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
prelude: >
|
||||||
|
Existing subnets that were created outside of a subnet pool can know
|
||||||
|
be moved, or "onboarded" into an existing subnet pool. This provides
|
||||||
|
a way for subnets to be brought under the management of a subnet pool
|
||||||
|
and begin participating in an address scope. By enabling onboarding,
|
||||||
|
existing subnets can be used with features that build on subnet pools
|
||||||
|
and address scopes. Subnet onboarding is subject to all the same
|
||||||
|
restrictions as and guarantees currently enforced by subnet pools
|
||||||
|
and address scopes.
|
||||||
|
features:
|
||||||
|
- Existing subnets can now be moved into a subnet pool, and by extension
|
||||||
|
can be moved into address scopes they were not initially participating in.
|
Loading…
Reference in New Issue
Block a user