Ensure gateway is set for prefix delegated subnets

With [1] gateway is no longer set for subnet created
with prefix delegation, but when adding the subnet
to the router it fails as it expects gateway to be
set.

This patch ensures gateway is set temporary to the first IP
of the subnet as it used to be just like the temporary CIDR.
Also need to ensure dhcp configuration is skipped to avoid the
original issue[2].

[1] https://review.opendev.org/c/openstack/neutron/+/699465
[2] https://bugs.launchpad.net/neutron/+bug/1856675

Closes-Bug: #1962306
Related-Bug: #1856675
Change-Id: I512f7d98ac99bb0ef06fd2acba09482e3436d18d
(cherry picked from commit 820b2e2665)
This commit is contained in:
yatinkarel 2022-02-28 18:54:26 +05:30 committed by yatin
parent ca458d9eb9
commit 6f01d45c4a
9 changed files with 25 additions and 8 deletions

View File

@ -1429,7 +1429,8 @@ class DeviceManager(object):
skip_subnet = ( skip_subnet = (
subnet.ip_version != ip_version or subnet.ip_version != ip_version or
not subnet.enable_dhcp or not subnet.enable_dhcp or
subnet.gateway_ip is None) subnet.gateway_ip is None or
subnet.subnetpool_id == constants.IPV6_PD_POOL_ID)
if skip_subnet: if skip_subnet:
continue continue

View File

@ -160,7 +160,7 @@ class DbBasePluginCommon(object):
'standard_attr_id': standard_attr_id, 'standard_attr_id': standard_attr_id,
} }
res['gateway_ip'] = str( res['gateway_ip'] = str(
subnet['gateway_ip']) if subnet['gateway_ip'] else None subnet['gateway_ip']) if subnet['gateway_ip'] is not None else None
# TODO(korzen) this method can get subnet as DB object or Subnet OVO, # TODO(korzen) this method can get subnet as DB object or Subnet OVO,
# so temporary workaround will be to fill in the fields in separate # so temporary workaround will be to fill in the fields in separate
# ways. After converting all code pieces to use Subnet OVO, the latter # ways. After converting all code pieces to use Subnet OVO, the latter

View File

@ -58,9 +58,6 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
if subnet.get('gateway_ip') is const.ATTR_NOT_SPECIFIED: if subnet.get('gateway_ip') is const.ATTR_NOT_SPECIFIED:
if subnet.get('ip_version') == const.IP_VERSION_6: if subnet.get('ip_version') == const.IP_VERSION_6:
gateway_ip = netaddr.IPNetwork(cidr_net).network gateway_ip = netaddr.IPNetwork(cidr_net).network
pd_net = netaddr.IPNetwork(const.PROVISIONAL_IPV6_PD_PREFIX)
if gateway_ip == pd_net.network:
return
else: else:
gateway_ip = netaddr.IPNetwork(cidr_net).network + 1 gateway_ip = netaddr.IPNetwork(cidr_net).network + 1
return str(gateway_ip) return str(gateway_ip)

View File

@ -87,7 +87,12 @@ class TestSubnet(base.BaseFullStackTestCase):
ipv6_ra_mode='slaac', ipv6_ra_mode='slaac',
subnetpool_id='prefix_delegation') subnetpool_id='prefix_delegation')
subnet = self._show_subnet(subnet['id']) subnet = self._show_subnet(subnet['id'])
self.assertIsNone(subnet['subnet']['gateway_ip']) cidr = subnet['subnet']['cidr']
self.assertEqual(subnet['subnet']['gateway_ip'],
str(netaddr.IPNetwork(cidr).network))
router = self.safe_client.create_router(self._project_id)
self.safe_client.add_router_interface(
router['id'], subnet['subnet']['id'])
def test_create_subnet_ipv4_with_subnetpool(self): def test_create_subnet_ipv4_with_subnetpool(self):
subnetpool_cidr = self.useFixture( subnetpool_cidr = self.useFixture(

View File

@ -16,6 +16,7 @@ from unittest import mock
from neutron_lib import constants from neutron_lib import constants
from oslo_config import cfg from oslo_config import cfg
from oslo_utils import uuidutils
from neutron.agent.linux import dhcp from neutron.agent.linux import dhcp
from neutron.agent.linux import ip_lib from neutron.agent.linux import ip_lib
@ -47,6 +48,7 @@ class TestDhcp(functional_base.BaseSudoTestCase):
def test_cleanup_stale_devices(self): def test_cleanup_stale_devices(self):
plugin = mock.MagicMock() plugin = mock.MagicMock()
dev_mgr = dhcp.DeviceManager(self.conf, plugin) dev_mgr = dhcp.DeviceManager(self.conf, plugin)
spool_id = uuidutils.generate_uuid()
network = { network = {
'id': 'foo_id', 'id': 'foo_id',
'tenant_id': 'foo_tenant', 'tenant_id': 'foo_tenant',
@ -57,6 +59,7 @@ class TestDhcp(functional_base.BaseSudoTestCase):
'ipv6_address_mode': None, 'ipv6_address_mode': None,
'ipv6_ra_mode': None, 'ipv6_ra_mode': None,
'cidr': '10.0.0.0/24', 'cidr': '10.0.0.0/24',
'subnetpool_id': spool_id,
'ip_version': 'ip_version':
constants.IP_VERSION_4, constants.IP_VERSION_4,
'gateway_ip': '10.0.0.1'})]} 'gateway_ip': '10.0.0.1'})]}

View File

@ -108,6 +108,7 @@ class DHCPAgentOVSTestFramework(base.BaseSudoTestCase):
ip_version=lib_const.IP_VERSION_4, ip_version=lib_const.IP_VERSION_4,
prefix_override=None): prefix_override=None):
cidr = self._IP_ADDRS[ip_version]['cidr'] cidr = self._IP_ADDRS[ip_version]['cidr']
spool_id = uuidutils.generate_uuid()
if prefix_override is not None: if prefix_override is not None:
cidr = '/'.join((cidr.split('/')[0], str(prefix_override))) cidr = '/'.join((cidr.split('/')[0], str(prefix_override)))
sn_dict = dhcp.DictModel( sn_dict = dhcp.DictModel(
@ -115,6 +116,7 @@ class DHCPAgentOVSTestFramework(base.BaseSudoTestCase):
network_id=net_id, network_id=net_id,
ip_version=ip_version, ip_version=ip_version,
cidr=cidr, cidr=cidr,
subnetpool_id=spool_id,
gateway_ip=self._IP_ADDRS[ip_version]['gateway'], gateway_ip=self._IP_ADDRS[ip_version]['gateway'],
enable_dhcp=dhcp_enabled, enable_dhcp=dhcp_enabled,
dns_nameservers=[], dns_nameservers=[],

View File

@ -53,6 +53,8 @@ FAKE_NETWORK_UUID = '12345678-1234-5678-1234567890ab'
FAKE_NETWORK_DHCP_NS = "qdhcp-%s" % FAKE_NETWORK_UUID FAKE_NETWORK_DHCP_NS = "qdhcp-%s" % FAKE_NETWORK_UUID
FAKE_TENANT_ID = 'aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa' FAKE_TENANT_ID = 'aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa'
FAKE_PRIORITY = 6 FAKE_PRIORITY = 6
FAKE_V4_SUBNETPOOL_ID = 'kkkkkkkk-kkkk-kkkk-kkkk-kkkkkkkkkkkk'
FAKE_V6_SUBNETPOOL_ID = 'jjjjjjjj-jjjj-jjjj-jjjj-jjjjjjjjjjjj'
fake_subnet1_allocation_pools = dhcp.DictModel(id='', start='172.9.9.2', fake_subnet1_allocation_pools = dhcp.DictModel(id='', start='172.9.9.2',
@ -64,6 +66,7 @@ fake_subnet1 = dhcp.DictModel(id='bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbb',
gateway_ip='172.9.9.1', host_routes=[], gateway_ip='172.9.9.1', host_routes=[],
dns_nameservers=[], dns_nameservers=[],
ip_version=const.IP_VERSION_4, ip_version=const.IP_VERSION_4,
subnetpool_id=FAKE_V4_SUBNETPOOL_ID,
ipv6_ra_mode=None, ipv6_address_mode=None, ipv6_ra_mode=None, ipv6_address_mode=None,
allocation_pools=fake_subnet1_allocation_pools) allocation_pools=fake_subnet1_allocation_pools)
@ -1744,6 +1747,7 @@ class FakeV4Subnet(object):
self.cidr = '192.168.0.0/24' self.cidr = '192.168.0.0/24'
self.gateway_ip = '192.168.0.1' self.gateway_ip = '192.168.0.1'
self.enable_dhcp = True self.enable_dhcp = True
self.subnetpool_id = FAKE_V4_SUBNETPOOL_ID
class FakeV6Subnet(object): class FakeV6Subnet(object):
@ -1753,6 +1757,7 @@ class FakeV6Subnet(object):
self.cidr = '2001:db8:0:1::/64' self.cidr = '2001:db8:0:1::/64'
self.gateway_ip = '2001:db8:0:1::1' self.gateway_ip = '2001:db8:0:1::1'
self.enable_dhcp = True self.enable_dhcp = True
self.subnetpool_id = FAKE_V6_SUBNETPOOL_ID
class FakeV4SubnetOutsideGateway(FakeV4Subnet): class FakeV4SubnetOutsideGateway(FakeV4Subnet):

View File

@ -436,6 +436,7 @@ class FakeV4Subnet(Dictable):
self.enable_dhcp = True self.enable_dhcp = True
self.host_routes = [FakeV4HostRoute()] self.host_routes = [FakeV4HostRoute()]
self.dns_nameservers = ['8.8.8.8'] self.dns_nameservers = ['8.8.8.8']
self.subnetpool_id = 'kkkkkkkk-kkkk-kkkk-kkkk-kkkkkkkkkkkk'
class FakeV4Subnet2(FakeV4Subnet): class FakeV4Subnet2(FakeV4Subnet):
@ -563,6 +564,7 @@ class FakeV6Subnet(object):
self.dns_nameservers = ['2001:0200:feed:7ac0::1'] self.dns_nameservers = ['2001:0200:feed:7ac0::1']
self.ipv6_ra_mode = None self.ipv6_ra_mode = None
self.ipv6_address_mode = None self.ipv6_address_mode = None
self.subnetpool_id = 'jjjjjjjj-jjjj-jjjj-jjjj-jjjjjjjjjjjj'
class FakeV4SubnetNoDHCP(object): class FakeV4SubnetNoDHCP(object):
@ -587,6 +589,7 @@ class FakeV6SubnetDHCPStateful(Dictable):
self.dns_nameservers = ['2001:0200:feed:7ac0::1'] self.dns_nameservers = ['2001:0200:feed:7ac0::1']
self.ipv6_ra_mode = None self.ipv6_ra_mode = None
self.ipv6_address_mode = constants.DHCPV6_STATEFUL self.ipv6_address_mode = constants.DHCPV6_STATEFUL
self.subnetpool_id = 'mmmmmmmm-mmmm-mmmm-mmmm-mmmmmmmmmmmm'
class FakeV6SubnetSlaac(object): class FakeV6SubnetSlaac(object):

View File

@ -3971,8 +3971,9 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
ipv6_ra_mode=constants.DHCPV6_STATELESS, ipv6_ra_mode=constants.DHCPV6_STATELESS,
ipv6_address_mode=constants.DHCPV6_STATELESS) ipv6_address_mode=constants.DHCPV6_STATELESS)
# If gateway_ip is not specified and the subnet is using prefix # If gateway_ip is not specified and the subnet is using prefix
# delegation, until the CIDR is assigned, this value should be "None" # delegation, until the CIDR is assigned, this value should be first
expected = {'gateway_ip': None, # IP from the subnet
expected = {'gateway_ip': str(netaddr.IPNetwork(cidr).network),
'cidr': cidr} 'cidr': cidr}
self._test_create_subnet(expected=expected, self._test_create_subnet(expected=expected,
cidr=cidr, ip_version=constants.IP_VERSION_6, cidr=cidr, ip_version=constants.IP_VERSION_6,