Implement default subnet pool configuration settings
The default_ipv6_subnet_pool option was added [1] as an integration point between prefix delegation work and subnet allocation work. This patch completes the integration with subnet allocation. This addresses the use case where a deployer wants all ipv6 addresses to come -- by default -- from a globally routable pool of ipv6 addresses. In a deployment with this option set, an API user can still access the old behavior by passing None explicitly as subnetpool_id when creating a subnet. This patch also adds the default_ipv4_subnet_pool for completeness. [1] https://review.openstack.org/#/c/166973 Change-Id: I301189b5cd31d7c5fa4a40fa3e04f8e6ac77592b Partially-Implements: blueprint subnet-allocation
This commit is contained in:
parent
fb8ea72240
commit
3428594556
|
@ -146,10 +146,20 @@ lock_path = $state_path/lock
|
|||
# Maximum number of routes per router
|
||||
# max_routes = 30
|
||||
|
||||
# Default Subnet Pool to be used for IPv4 subnet-allocation.
|
||||
# Specifies by UUID the pool to be used in case of subnet-create being called
|
||||
# without a subnet-pool ID. The default of None means that no pool will be
|
||||
# used unless passed explicitly to subnet create. If no pool is used, then a
|
||||
# CIDR must be passed to create a subnet and that subnet will not be allocated
|
||||
# from any pool; it will be considered part of the tenant's private address
|
||||
# space.
|
||||
# default_ipv4_subnet_pool =
|
||||
|
||||
# Default Subnet Pool to be used for IPv6 subnet-allocation.
|
||||
# Specifies by UUID the pool to be used in case of subnet-create being
|
||||
# called without CIDR or subnet-pool ID. Set to "prefix_delegation"
|
||||
# called without a subnet-pool ID. Set to "prefix_delegation"
|
||||
# to enable IPv6 Prefix Delegation in a PD-capable environment.
|
||||
# See the description for default_ipv4_subnet_pool for more information.
|
||||
# default_ipv6_subnet_pool =
|
||||
|
||||
# =========== items for MTU selection and advertisement =============
|
||||
|
|
|
@ -782,7 +782,7 @@ RESOURCE_ATTRIBUTE_MAP = {
|
|||
'allow_put': False,
|
||||
'default': ATTR_NOT_SPECIFIED,
|
||||
'required_by_policy': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'validate': {'type:uuid_or_none': None},
|
||||
'is_visible': True},
|
||||
'prefixlen': {'allow_post': True,
|
||||
'allow_put': False,
|
||||
|
|
|
@ -71,9 +71,12 @@ core_opts = [
|
|||
help=_("Maximum number of host routes per subnet")),
|
||||
cfg.IntOpt('max_fixed_ips_per_port', default=5,
|
||||
help=_("Maximum number of fixed ips per port")),
|
||||
cfg.IntOpt('default_ipv6_subnet_pool', default=None,
|
||||
help=_("Default subnet-pool to be used for automatic subnet "
|
||||
"CIDR allocation")),
|
||||
cfg.StrOpt('default_ipv4_subnet_pool', default=None,
|
||||
help=_("Default IPv4 subnet-pool to be used for automatic "
|
||||
"subnet CIDR allocation")),
|
||||
cfg.StrOpt('default_ipv6_subnet_pool', default=None,
|
||||
help=_("Default IPv6 subnet-pool to be used for automatic "
|
||||
"subnet CIDR allocation")),
|
||||
cfg.IntOpt('dhcp_lease_duration', default=86400,
|
||||
deprecated_name='dhcp_lease_time',
|
||||
help=_("DHCP lease duration (in seconds). Use -1 to tell "
|
||||
|
|
|
@ -1212,7 +1212,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
|||
@oslo_db_api.wrap_db_retry(max_retries=db_api.MAX_RETRIES,
|
||||
retry_on_request=True,
|
||||
retry_on_deadlock=True)
|
||||
def _create_subnet_from_pool(self, context, subnet):
|
||||
def _create_subnet_from_pool(self, context, subnet, subnetpool_id):
|
||||
s = subnet['subnet']
|
||||
tenant_id = self._get_tenant_id_for_create(context, s)
|
||||
has_allocpool = attributes.is_attr_set(s['allocation_pools'])
|
||||
|
@ -1223,7 +1223,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
|||
raise n_exc.BadRequest(resource='subnets', msg=reason)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
subnetpool = self._get_subnetpool(context, s['subnetpool_id'])
|
||||
subnetpool = self._get_subnetpool(context, subnetpool_id)
|
||||
network = self._get_network(context, s["network_id"])
|
||||
allocator = subnet_alloc.SubnetAllocator(subnetpool)
|
||||
req = self._make_subnet_request(tenant_id, s, subnetpool)
|
||||
|
@ -1273,6 +1273,39 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
|||
subnet['network_id'])
|
||||
return self._make_subnet_dict(subnet)
|
||||
|
||||
def _get_subnetpool_id(self, subnet):
|
||||
"""Returns the subnetpool id for this request
|
||||
|
||||
If the pool id was explicitly set in the request then that will be
|
||||
returned, even if it is None.
|
||||
|
||||
Otherwise, the default pool for the IP version requested will be
|
||||
returned. This will either be a pool id or None (the default for each
|
||||
configuration parameter). This implies that the ip version must be
|
||||
either set implicitly with a specific cidr or explicitly using
|
||||
ip_version attribute.
|
||||
|
||||
:param subnet: The subnet dict from the request
|
||||
"""
|
||||
subnetpool_id = subnet.get('subnetpool_id',
|
||||
attributes.ATTR_NOT_SPECIFIED)
|
||||
if subnetpool_id != attributes.ATTR_NOT_SPECIFIED:
|
||||
return subnetpool_id
|
||||
|
||||
cidr = subnet.get('cidr')
|
||||
if attributes.is_attr_set(cidr):
|
||||
ip_version = netaddr.IPNetwork(cidr).version
|
||||
else:
|
||||
ip_version = subnet.get('ip_version')
|
||||
if not attributes.is_attr_set(ip_version):
|
||||
msg = _('ip_version must be specified in the absence of '
|
||||
'cidr and subnetpool_id')
|
||||
raise n_exc.BadRequest(resource='subnets', msg=msg)
|
||||
|
||||
if ip_version == 4:
|
||||
return cfg.CONF.default_ipv4_subnet_pool
|
||||
return cfg.CONF.default_ipv6_subnet_pool
|
||||
|
||||
def create_subnet(self, context, subnet):
|
||||
|
||||
s = subnet['subnet']
|
||||
|
@ -1290,11 +1323,15 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
|||
net = netaddr.IPNetwork(s['cidr'])
|
||||
subnet['subnet']['cidr'] = '%s/%s' % (net.network, net.prefixlen)
|
||||
|
||||
subnetpool_id = s.get('subnetpool_id', attributes.ATTR_NOT_SPECIFIED)
|
||||
if not attributes.is_attr_set(subnetpool_id):
|
||||
subnetpool_id = self._get_subnetpool_id(s)
|
||||
if not subnetpool_id:
|
||||
if not has_cidr:
|
||||
msg = _('A cidr must be specified in the absence of a '
|
||||
'subnet pool')
|
||||
raise n_exc.BadRequest(resource='subnets', msg=msg)
|
||||
# Create subnet from the implicit(AKA null) pool
|
||||
return self._create_subnet_from_implicit_pool(context, subnet)
|
||||
return self._create_subnet_from_pool(context, subnet)
|
||||
return self._create_subnet_from_pool(context, subnet, subnetpool_id)
|
||||
|
||||
def _update_subnet_dns_nameservers(self, context, id, s):
|
||||
old_dns_list = self._get_dns_by_subnet(context, id)
|
||||
|
|
|
@ -215,6 +215,8 @@ class ContrailPluginTestCase(test_plugin.NeutronDbPluginV2TestCase):
|
|||
def setUp(self, plugin=None, ext_mgr=None):
|
||||
if 'v6' in self._testMethodName:
|
||||
self.skipTest("OpenContrail Plugin does not support IPV6.")
|
||||
if 'test_create_subnet_only_ip_version' in self._testMethodName:
|
||||
self.skipTest("OpenContrail Plugin does not support subnet pools.")
|
||||
cfg.CONF.keystone_authtoken = KeyStoneInfo()
|
||||
mock.patch('requests.post').start().side_effect = FAKE_SERVER.request
|
||||
super(ContrailPluginTestCase, self).setUp(self._plugin_name)
|
||||
|
|
|
@ -2811,6 +2811,74 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
|
|||
res = subnet_req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, webob.exc.HTTPClientError.code)
|
||||
|
||||
def test_create_subnet_no_ip_version(self):
|
||||
with self.network() as network:
|
||||
cfg.CONF.set_override('default_ipv4_subnet_pool', None)
|
||||
cfg.CONF.set_override('default_ipv6_subnet_pool', None)
|
||||
data = {'subnet': {'network_id': network['network']['id'],
|
||||
'tenant_id': network['network']['tenant_id']}}
|
||||
subnet_req = self.new_create_request('subnets', data)
|
||||
res = subnet_req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, webob.exc.HTTPClientError.code)
|
||||
|
||||
def test_create_subnet_only_ip_version_v6_no_pool(self):
|
||||
with self.network() as network:
|
||||
tenant_id = network['network']['tenant_id']
|
||||
cfg.CONF.set_override('default_ipv6_subnet_pool', None)
|
||||
data = {'subnet': {'network_id': network['network']['id'],
|
||||
'ip_version': '6',
|
||||
'tenant_id': tenant_id}}
|
||||
subnet_req = self.new_create_request('subnets', data)
|
||||
res = subnet_req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, webob.exc.HTTPClientError.code)
|
||||
|
||||
def test_create_subnet_only_ip_version_v4(self):
|
||||
with self.network() as network:
|
||||
tenant_id = network['network']['tenant_id']
|
||||
subnetpool_prefix = '10.0.0.0/8'
|
||||
with self.subnetpool(prefixes=[subnetpool_prefix],
|
||||
admin=False,
|
||||
name="My subnet pool",
|
||||
tenant_id=tenant_id,
|
||||
min_prefixlen='25') as subnetpool:
|
||||
subnetpool_id = subnetpool['subnetpool']['id']
|
||||
cfg.CONF.set_override('default_ipv4_subnet_pool',
|
||||
subnetpool_id)
|
||||
data = {'subnet': {'network_id': network['network']['id'],
|
||||
'ip_version': '4',
|
||||
'prefixlen': '27',
|
||||
'tenant_id': tenant_id}}
|
||||
subnet_req = self.new_create_request('subnets', data)
|
||||
res = subnet_req.get_response(self.api)
|
||||
subnet = self.deserialize(self.fmt, res)['subnet']
|
||||
ip_net = netaddr.IPNetwork(subnet['cidr'])
|
||||
self.assertTrue(ip_net in netaddr.IPNetwork(subnetpool_prefix))
|
||||
self.assertEqual(27, ip_net.prefixlen)
|
||||
self.assertEqual(subnetpool_id, subnet['subnetpool_id'])
|
||||
|
||||
def test_create_subnet_only_ip_version_v6(self):
|
||||
with self.network() as network:
|
||||
tenant_id = network['network']['tenant_id']
|
||||
subnetpool_prefix = '2000::/56'
|
||||
with self.subnetpool(prefixes=[subnetpool_prefix],
|
||||
admin=False,
|
||||
name="My ipv6 subnet pool",
|
||||
tenant_id=tenant_id,
|
||||
min_prefixlen='64') as subnetpool:
|
||||
subnetpool_id = subnetpool['subnetpool']['id']
|
||||
cfg.CONF.set_override('default_ipv6_subnet_pool',
|
||||
subnetpool_id)
|
||||
data = {'subnet': {'network_id': network['network']['id'],
|
||||
'ip_version': '6',
|
||||
'tenant_id': tenant_id}}
|
||||
subnet_req = self.new_create_request('subnets', data)
|
||||
res = subnet_req.get_response(self.api)
|
||||
subnet = self.deserialize(self.fmt, res)['subnet']
|
||||
self.assertEqual(subnetpool_id, subnet['subnetpool_id'])
|
||||
ip_net = netaddr.IPNetwork(subnet['cidr'])
|
||||
self.assertTrue(ip_net in netaddr.IPNetwork(subnetpool_prefix))
|
||||
self.assertEqual(64, ip_net.prefixlen)
|
||||
|
||||
def test_create_subnet_bad_V4_cidr_prefix_len(self):
|
||||
with self.network() as network:
|
||||
data = {'subnet': {'network_id': network['network']['id'],
|
||||
|
|
Loading…
Reference in New Issue