Allow empty gateway IP in subnets from subnet pools
When a subnet is created from a subnet pool, now is possible to undefine the gateway IP. The new subnet created will have this value assigned to None. $ openstack subnet create --subnet-pool shared-default-subnetpool-v4 \ --network net14 snet14 --gateway None --format value \ --column gateway_ip None Closes-Bug: #2112453 Change-Id: I3bdd260f0f6b0259ff15cfe16a111bfe93b40749
This commit is contained in:

committed by
Rodolfo Alonso

parent
7c478bad67
commit
5cccd2112f
@ -36,7 +36,8 @@ class SubnetRequest(metaclass=abc.ABCMeta):
|
||||
instantiated on its own. Rather, a subclass of this class should be used.
|
||||
"""
|
||||
def __init__(self, tenant_id, subnet_id,
|
||||
gateway_ip=None, allocation_pools=None):
|
||||
gateway_ip=None, allocation_pools=None,
|
||||
set_gateway_ip=True):
|
||||
"""Initialize and validate
|
||||
|
||||
:param tenant_id: The tenant id who will own the subnet
|
||||
@ -50,10 +51,16 @@ class SubnetRequest(metaclass=abc.ABCMeta):
|
||||
of this range if specifically requested.
|
||||
:type allocation_pools: A list of netaddr.IPRange. None if not
|
||||
specified.
|
||||
:param set_gateway_ip: in case the ``gateway_ip`` value is not defined
|
||||
(None), the IPAM module will set an IP address within the range of
|
||||
the subnet CIDR. If ``set_gateway_ip`` is unset, no IP address will
|
||||
be assigned.
|
||||
:type set_gateway_ip: boolean
|
||||
"""
|
||||
self._tenant_id = tenant_id
|
||||
self._subnet_id = subnet_id
|
||||
self._gateway_ip = None
|
||||
self._set_gateway_ip = set_gateway_ip
|
||||
self._allocation_pools = None
|
||||
|
||||
if gateway_ip is not None:
|
||||
@ -97,6 +104,10 @@ class SubnetRequest(metaclass=abc.ABCMeta):
|
||||
def gateway_ip(self):
|
||||
return self._gateway_ip
|
||||
|
||||
@property
|
||||
def set_gateway_ip(self):
|
||||
return self._set_gateway_ip
|
||||
|
||||
@property
|
||||
def allocation_pools(self):
|
||||
return self._allocation_pools
|
||||
@ -144,7 +155,8 @@ class AnySubnetRequest(SubnetRequest):
|
||||
constants.IPv6: '::'}
|
||||
|
||||
def __init__(self, tenant_id, subnet_id, version, prefixlen,
|
||||
gateway_ip=None, allocation_pools=None):
|
||||
gateway_ip=None, allocation_pools=None,
|
||||
set_gateway_ip=True):
|
||||
"""Initialize AnySubnetRequest
|
||||
|
||||
:param version: Either constants.IPv4 or constants.IPv6
|
||||
@ -156,7 +168,9 @@ class AnySubnetRequest(SubnetRequest):
|
||||
tenant_id=tenant_id,
|
||||
subnet_id=subnet_id,
|
||||
gateway_ip=gateway_ip,
|
||||
allocation_pools=allocation_pools)
|
||||
allocation_pools=allocation_pools,
|
||||
set_gateway_ip=set_gateway_ip,
|
||||
)
|
||||
|
||||
net = netaddr.IPNetwork(self.WILDCARDS[version] + '/' + str(prefixlen))
|
||||
self._validate_with_subnet(net)
|
||||
@ -176,7 +190,8 @@ class SpecificSubnetRequest(SubnetRequest):
|
||||
blueprints.
|
||||
"""
|
||||
def __init__(self, tenant_id, subnet_id, subnet_cidr,
|
||||
gateway_ip=None, allocation_pools=None):
|
||||
gateway_ip=None, allocation_pools=None,
|
||||
set_gateway_ip=True):
|
||||
"""Initialize SpecificSubnetRequest
|
||||
|
||||
:param subnet: The subnet requested. Can be IPv4 or IPv6. However,
|
||||
@ -188,7 +203,9 @@ class SpecificSubnetRequest(SubnetRequest):
|
||||
tenant_id=tenant_id,
|
||||
subnet_id=subnet_id,
|
||||
gateway_ip=gateway_ip,
|
||||
allocation_pools=allocation_pools)
|
||||
allocation_pools=allocation_pools,
|
||||
set_gateway_ip=set_gateway_ip,
|
||||
)
|
||||
|
||||
self._subnet_cidr = netaddr.IPNetwork(subnet_cidr)
|
||||
self._validate_with_subnet(self._subnet_cidr)
|
||||
@ -322,6 +339,7 @@ class SubnetRequestFactory:
|
||||
cidr = subnet.get('cidr')
|
||||
cidr = cidr if validators.is_attr_set(cidr) else None
|
||||
gateway_ip = subnet.get('gateway_ip')
|
||||
set_gateway_ip = gateway_ip is not None
|
||||
gateway_ip = gateway_ip if validators.is_attr_set(gateway_ip) else None
|
||||
subnet_id = subnet.get('id', uuidutils.generate_uuid())
|
||||
|
||||
@ -335,7 +353,9 @@ class SubnetRequestFactory:
|
||||
subnet['tenant_id'],
|
||||
subnet_id,
|
||||
common_utils.ip_version_from_int(subnetpool['ip_version']),
|
||||
prefixlen)
|
||||
prefixlen,
|
||||
set_gateway_ip=set_gateway_ip,
|
||||
)
|
||||
alloc_pools = subnet.get('allocation_pools')
|
||||
alloc_pools = (
|
||||
alloc_pools if validators.is_attr_set(alloc_pools) else None)
|
||||
@ -353,4 +373,6 @@ class SubnetRequestFactory:
|
||||
subnet_id,
|
||||
cidr,
|
||||
gateway_ip=gateway_ip,
|
||||
allocation_pools=alloc_pools)
|
||||
allocation_pools=alloc_pools,
|
||||
set_gateway_ip=set_gateway_ip,
|
||||
)
|
||||
|
@ -129,7 +129,7 @@ class SubnetAllocator(driver.Pool):
|
||||
if request.prefixlen >= prefix.prefixlen:
|
||||
subnet = next(prefix.subnet(request.prefixlen))
|
||||
gateway_ip = request.gateway_ip
|
||||
if not gateway_ip:
|
||||
if not gateway_ip and request.set_gateway_ip:
|
||||
gateway_ip = subnet.network + 1
|
||||
pools = ipam_utils.generate_pools(subnet.cidr,
|
||||
gateway_ip)
|
||||
@ -138,7 +138,9 @@ class SubnetAllocator(driver.Pool):
|
||||
request.subnet_id,
|
||||
subnet.cidr,
|
||||
gateway_ip=gateway_ip,
|
||||
allocation_pools=pools)
|
||||
allocation_pools=pools,
|
||||
set_gateway_ip=request.set_gateway_ip,
|
||||
)
|
||||
msg = _("Insufficient prefix space to allocate subnet size /%s")
|
||||
raise exceptions.SubnetAllocationError(
|
||||
reason=msg % str(request.prefixlen))
|
||||
@ -156,7 +158,9 @@ class SubnetAllocator(driver.Pool):
|
||||
request.subnet_id,
|
||||
cidr,
|
||||
gateway_ip=request.gateway_ip,
|
||||
allocation_pools=request.allocation_pools)
|
||||
allocation_pools=request.allocation_pools,
|
||||
set_gateway_ip=request.set_gateway_ip,
|
||||
)
|
||||
msg = _("Cannot allocate requested subnet from the available "
|
||||
"set of prefixes")
|
||||
raise exceptions.SubnetAllocationError(reason=msg)
|
||||
@ -200,13 +204,17 @@ class IpamSubnet(driver.Subnet):
|
||||
subnet_id,
|
||||
cidr,
|
||||
gateway_ip=None,
|
||||
allocation_pools=None):
|
||||
allocation_pools=None,
|
||||
set_gateway_ip=True,
|
||||
):
|
||||
self._req = ipam_req.SpecificSubnetRequest(
|
||||
tenant_id,
|
||||
subnet_id,
|
||||
cidr,
|
||||
gateway_ip=gateway_ip,
|
||||
allocation_pools=allocation_pools)
|
||||
allocation_pools=allocation_pools,
|
||||
set_gateway_ip=set_gateway_ip,
|
||||
)
|
||||
|
||||
def allocate(self, address_request):
|
||||
raise NotImplementedError()
|
||||
|
@ -6456,6 +6456,28 @@ class TestSubnetPoolsV2(NeutronDbPluginV2TestCase):
|
||||
self.assertEqual(subnet.prefixlen,
|
||||
int(sp['subnetpool']['default_prefixlen']))
|
||||
|
||||
def test_allocate_any_subnet_with_default_prefixlen_no_gateway_ip(self):
|
||||
with self.network() as network:
|
||||
sp = self._test_create_subnetpool(['10.10.0.0/16'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='21')
|
||||
|
||||
# Request any subnet allocation using default prefix
|
||||
data = {'subnet': {'network_id': network['network']['id'],
|
||||
'subnetpool_id': sp['subnetpool']['id'],
|
||||
'ip_version': constants.IP_VERSION_4,
|
||||
'tenant_id': network['network']['tenant_id'],
|
||||
'gateway_ip': None,
|
||||
}}
|
||||
req = self.new_create_request('subnets', data)
|
||||
res = self.deserialize(self.fmt, req.get_response(self.api))
|
||||
|
||||
subnet = netaddr.IPNetwork(res['subnet']['cidr'])
|
||||
self.assertEqual(subnet.prefixlen,
|
||||
int(sp['subnetpool']['default_prefixlen']))
|
||||
self.assertIsNone(res['subnet']['gateway_ip'])
|
||||
|
||||
def test_allocate_specific_subnet_with_mismatch_prefixlen(self):
|
||||
with self.network() as network:
|
||||
sp = self._test_create_subnetpool(['10.10.0.0/16'],
|
||||
@ -6646,6 +6668,29 @@ class TestSubnetPoolsV2(NeutronDbPluginV2TestCase):
|
||||
res = req.get_response(self.api)
|
||||
self._check_http_response(res, 400)
|
||||
|
||||
def test_allocate_specific_subnet_no_gateway_ip(self):
|
||||
with self.network() as network:
|
||||
sp = self._test_create_subnetpool(['10.10.0.0/16'],
|
||||
tenant_id=self._tenant_id,
|
||||
name=self._POOL_NAME,
|
||||
min_prefixlen='21')
|
||||
|
||||
# Request a specific subnet allocation
|
||||
data = {'subnet': {'network_id': network['network']['id'],
|
||||
'subnetpool_id': sp['subnetpool']['id'],
|
||||
'cidr': '10.10.1.0/24',
|
||||
'ip_version': constants.IP_VERSION_4,
|
||||
'tenant_id': network['network']['tenant_id'],
|
||||
'gateway_ip': None,
|
||||
}}
|
||||
req = self.new_create_request('subnets', data)
|
||||
res = self.deserialize(self.fmt, req.get_response(self.api))
|
||||
|
||||
# Assert the allocated subnet CIDR is what we expect
|
||||
subnet = netaddr.IPNetwork(res['subnet']['cidr'])
|
||||
self.assertEqual(netaddr.IPNetwork('10.10.1.0/24'), subnet)
|
||||
self.assertIsNone(res['subnet']['gateway_ip'])
|
||||
|
||||
def test_delete_subnetpool_existing_allocations(self):
|
||||
with self.network() as network:
|
||||
sp = self._test_create_subnetpool(['10.10.0.0/16'],
|
||||
|
@ -197,3 +197,34 @@ class TestSubnetAllocation(testlib_api.SqlTestCase):
|
||||
'fe80::/63')
|
||||
with mock.patch("sqlalchemy.orm.query.Query.update", return_value=0):
|
||||
self.assertRaises(db_exc.RetryRequest, sa.allocate_subnet, req)
|
||||
|
||||
def test_subnetpool_any_request_no_gateway_ip_set(self):
|
||||
sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp',
|
||||
['10.1.0.0/16', '192.168.1.0/24'],
|
||||
21, 4)
|
||||
sp = self.plugin._get_subnetpool(self.ctx, sp['id'])
|
||||
with db_api.CONTEXT_WRITER.using(self.ctx):
|
||||
sa = subnet_alloc.SubnetAllocator(sp, self.ctx)
|
||||
req = ipam_req.AnySubnetRequest(self._tenant_id,
|
||||
uuidutils.generate_uuid(),
|
||||
constants.IPv4, 21,
|
||||
set_gateway_ip=False)
|
||||
res = sa.allocate_subnet(req)
|
||||
detail = res.get_details()
|
||||
self.assertIsNone(detail.gateway_ip)
|
||||
self.assertFalse(detail.set_gateway_ip)
|
||||
|
||||
def test_subnetpool_specific_request_no_gateway_ip_set(self):
|
||||
sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp',
|
||||
['10.1.0.0/16', '192.168.1.0/24'],
|
||||
21, 4)
|
||||
sp = self.plugin._get_subnetpool(self.ctx, sp['id'])
|
||||
with db_api.CONTEXT_WRITER.using(self.ctx):
|
||||
sa = subnet_alloc.SubnetAllocator(sp, self.ctx)
|
||||
req = ipam_req.SpecificSubnetRequest(
|
||||
self._tenant_id, uuidutils.generate_uuid(),
|
||||
'10.1.2.0/27', set_gateway_ip=False)
|
||||
res = sa.allocate_subnet(req)
|
||||
detail = res.get_details()
|
||||
self.assertIsNone(detail.gateway_ip)
|
||||
self.assertFalse(detail.set_gateway_ip)
|
||||
|
Reference in New Issue
Block a user