# 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 =============
'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,
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 "
@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'])
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)
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']
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)
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)
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'],