Using 31-Bit and 32-Bit prefixes for IPv4 reasonably
When needing to create a point to point connection via a subnet, generally and /31 is the recommended cidr. Neutron supports /31 disabling dhcp and gateway on a subnet. /32 is also supported in openstack. Closes-Bug: #1580927 Change-Id: I3bfa3efb9fb8076656b16c89d2f35d74efde12b7
This commit is contained in:
parent
535a13be9c
commit
437a311eca
@ -310,9 +310,17 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
|
||||
Finally, verify that each range fall within the subnet's CIDR.
|
||||
"""
|
||||
subnet = netaddr.IPNetwork(subnet_cidr)
|
||||
subnet_first_ip = netaddr.IPAddress(subnet.first + 1)
|
||||
# last address is broadcast in v4
|
||||
subnet_last_ip = netaddr.IPAddress(subnet.last - (subnet.version == 4))
|
||||
if subnet.version == const.IP_VERSION_4:
|
||||
if subnet.prefixlen <= 30:
|
||||
subnet_first_ip = netaddr.IPAddress(subnet.first + 1)
|
||||
# last address is broadcast in v4
|
||||
subnet_last_ip = netaddr.IPAddress(subnet.last - 1)
|
||||
else:
|
||||
subnet_first_ip = netaddr.IPAddress(subnet.first)
|
||||
subnet_last_ip = netaddr.IPAddress(subnet.last)
|
||||
else: # IPv6 case
|
||||
subnet_first_ip = netaddr.IPAddress(subnet.first + 1)
|
||||
subnet_last_ip = netaddr.IPAddress(subnet.last)
|
||||
|
||||
LOG.debug("Performing IP validity checks on allocation pools")
|
||||
ip_sets = []
|
||||
|
@ -62,12 +62,17 @@ def generate_pools(cidr, gateway_ip):
|
||||
if first == last:
|
||||
# handle single address subnet case
|
||||
return [netaddr.IPRange(first, last)]
|
||||
first_ip = first + 1
|
||||
# last address is broadcast in v4
|
||||
last_ip = last - (ip_version == 4)
|
||||
if first_ip >= last_ip:
|
||||
# /31 lands here
|
||||
return []
|
||||
if ip_version == constants.IP_VERSION_4:
|
||||
if net.prefixlen <= 30:
|
||||
first_ip = first + 1
|
||||
# last address is broadcast in v4
|
||||
last_ip = last - (ip_version == 4)
|
||||
else:
|
||||
first_ip = first
|
||||
last_ip = last
|
||||
else: # IPv6 case
|
||||
first_ip = first + 1
|
||||
last_ip = last
|
||||
ipset = netaddr.IPSet(netaddr.IPRange(first_ip, last_ip))
|
||||
if gateway_ip:
|
||||
ipset.remove(netaddr.IPAddress(gateway_ip, ip_version))
|
||||
|
@ -129,7 +129,10 @@ class FakeFullstackMachine(machine_fixtures.FakeMachineBase):
|
||||
def _configure_static_ipaddress(self):
|
||||
self.port.addr.add(self.ip_cidr)
|
||||
if self.gateway_ip:
|
||||
net_helpers.set_namespace_gateway(self.port, self.gateway_ip)
|
||||
net = netaddr.IPNetwork(self.ip_cidr)
|
||||
gateway_ip = netaddr.IPAddress(self.gateway_ip)
|
||||
if gateway_ip in net:
|
||||
net_helpers.set_namespace_gateway(self.port, self.gateway_ip)
|
||||
|
||||
def _configure_ipaddress_via_dhcp(self):
|
||||
self._start_async_dhclient()
|
||||
|
@ -20,6 +20,7 @@ from oslo_utils import uuidutils
|
||||
from neutron.tests.common.exclusive_resources import ip_network
|
||||
from neutron.tests.fullstack import base
|
||||
from neutron.tests.fullstack.resources import environment
|
||||
from neutron.tests.fullstack.resources import machine
|
||||
|
||||
|
||||
class TestSubnet(base.BaseFullStackTestCase):
|
||||
@ -132,3 +133,32 @@ class TestSubnet(base.BaseFullStackTestCase):
|
||||
subnet = self._show_subnet(subnet['id'])
|
||||
self.assertEqual(subnet['subnet']['cidr'], str(subnets[2].cidr))
|
||||
self.assertEqual(subnet['subnet']['gateway_ip'], str(gateway_ip))
|
||||
|
||||
def test_subnet_with_prefixlen_31_connectivity(self):
|
||||
network = self._create_network(self._project_id)
|
||||
self.safe_client.create_subnet(
|
||||
self._project_id, network['id'],
|
||||
cidr='10.14.0.20/31',
|
||||
gateway_ip='10.14.0.19',
|
||||
name='subnet-test',
|
||||
enable_dhcp=False)
|
||||
|
||||
vms = self._prepare_vms_in_net(self._project_id, network, False)
|
||||
vms.ping_all()
|
||||
|
||||
def test_subnet_with_prefixlen_32_vm_spawn(self):
|
||||
network = self._create_network(self._project_id)
|
||||
self.safe_client.create_subnet(
|
||||
self._project_id, network['id'],
|
||||
cidr='10.14.0.20/32',
|
||||
gateway_ip='10.14.0.19',
|
||||
name='subnet-test',
|
||||
enable_dhcp=False)
|
||||
|
||||
vm = self.useFixture(
|
||||
machine.FakeFullstackMachine(
|
||||
self.environment.hosts[0],
|
||||
network['id'],
|
||||
self._project_id,
|
||||
self.safe_client))
|
||||
vm.block_until_boot()
|
||||
|
@ -1693,16 +1693,6 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||
self.assertEqual(data['port']['fixed_ips'],
|
||||
res['port']['fixed_ips'])
|
||||
|
||||
def test_no_more_port_exception(self):
|
||||
with self.subnet(cidr='10.0.0.0/31', enable_dhcp=False,
|
||||
gateway_ip=None) as subnet:
|
||||
id = subnet['subnet']['network_id']
|
||||
res = self._create_port(self.fmt, id)
|
||||
data = self.deserialize(self.fmt, res)
|
||||
msg = str(lib_exc.IpAddressGenerationFailure(net_id=id))
|
||||
self.assertEqual(data['NeutronError']['message'], msg)
|
||||
self.assertEqual(webob.exc.HTTPConflict.code, res.status_int)
|
||||
|
||||
def test_create_ports_native_quotas(self):
|
||||
quota = 1
|
||||
cfg.CONF.set_override('quota_port', quota, group='QUOTAS')
|
||||
@ -5091,6 +5081,43 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(webob.exc.HTTPConflict.code, res.status_int)
|
||||
|
||||
def test_create_subnet_allocation_pools_with_prefixlen_31(self):
|
||||
with self.network() as network:
|
||||
with self.subnet(network=network,
|
||||
enable_dhcp=False,
|
||||
gateway_ip='10.14.0.19',
|
||||
cidr='10.14.0.20/31'):
|
||||
res = self._create_port(self.fmt, network['network']['id'])
|
||||
self.assertEqual(webob.exc.HTTPCreated.code, res.status_int)
|
||||
res = self._create_port(self.fmt, network['network']['id'])
|
||||
self.assertEqual(webob.exc.HTTPCreated.code, res.status_int)
|
||||
|
||||
def test_update_subnet_allocation_pools_with_prefixlen_31(self):
|
||||
with self.network() as network:
|
||||
with self.subnet(network=network,
|
||||
enable_dhcp=False,
|
||||
gateway_ip='10.14.0.19',
|
||||
cidr='10.14.0.20/31') as subnet:
|
||||
data = {'subnet': {'allocation_pools': [
|
||||
{'start': '10.14.0.20', 'end': '10.14.0.21'}]}}
|
||||
req = self.new_update_request('subnets', data,
|
||||
subnet['subnet']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(webob.exc.HTTPOk.code, res.status_int)
|
||||
res = self._create_port(self.fmt, network['network']['id'])
|
||||
self.assertEqual(webob.exc.HTTPCreated.code, res.status_int)
|
||||
res = self._create_port(self.fmt, network['network']['id'])
|
||||
self.assertEqual(webob.exc.HTTPCreated.code, res.status_int)
|
||||
|
||||
def test_create_subnet_allocation_pools_with_prefixlen_32(self):
|
||||
with self.network() as network:
|
||||
with self.subnet(network=network,
|
||||
enable_dhcp=False,
|
||||
gateway_ip='10.14.0.19',
|
||||
cidr='10.14.0.20/32'):
|
||||
res = self._create_port(self.fmt, network['network']['id'])
|
||||
self.assertEqual(webob.exc.HTTPCreated.code, res.status_int)
|
||||
|
||||
def test_create_subnets_native_quotas(self):
|
||||
quota = 1
|
||||
cfg.CONF.set_override('quota_subnet', quota, group='QUOTAS')
|
||||
|
@ -89,7 +89,7 @@ class TestIpamUtils(base.BaseTestCase):
|
||||
|
||||
def test_generate_pools_v4_31(self):
|
||||
cidr = '192.168.0.0/31'
|
||||
expected = []
|
||||
expected = [netaddr.IPRange('192.168.0.0', '192.168.0.1')]
|
||||
self.assertEqual(expected, utils.generate_pools(cidr, None))
|
||||
|
||||
def test_generate_pools_v4_gateway_middle(self):
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Neutron supports creating IPv4 subnet with prefixlen /31 and /32,
|
||||
via disabling dhcp on a subnet.
|
||||
For more information, see bug
|
||||
`1580927 <https://bugs.launchpad.net/neutron/+bug/1580927>`_.
|
Loading…
Reference in New Issue
Block a user