Fix IPv6 router on UC re-install

Running 'openstack undercloud install' a second
time failed with a ConflictException because a subnet
with a router port cannot have the gateway_ip updated.

Ensure that the router port is always deleted prior to
the subnet update and then add the router port again
after updating the subnet

Closes-Bug: #1955417
Change-Id: I0e075153e99876ddf613caecc92b4442e8b6ca03
(cherry picked from commit 62ded067d5)
This commit is contained in:
Harald Jensås 2021-12-20 22:44:08 +01:00
parent 40dcc728c6
commit 31a431d351
1 changed files with 56 additions and 19 deletions

View File

@ -175,16 +175,42 @@ def _neutron_segment_update(sdk, segment_id, name):
raise raise
def _ensure_neutron_router(sdk, name, subnet_id): def _ensure_neutron_router(sdk, subnet):
# If the router already exist, don't try to create it again.
if list(sdk.network.routers(name=name)):
return
try: try:
router = sdk.network.create_router(name=name, admin_state_up='true') router = sdk.network.find_router(name_or_id=subnet.name)
sdk.network.add_interface_to_router(router.id, subnet_id=subnet_id) if router is None:
sdk.network.create_router(name=subnet.name,
admin_state_up='true')
except Exception: except Exception:
print('ERROR: Create router for subnet %s failed.' % name) print('ERROR: Create router for subnet %s failed.' % subnet.name)
raise
def _add_router_port(sdk, subnet):
try:
router = sdk.network.find_router(name_or_id=subnet.name)
sdk.network.add_interface_to_router(router.id, subnet_id=subnet.id)
except Exception:
print('ERROR: Add router port for subnet %s failed.' % subnet.name)
raise
def _remove_router_port(sdk, subnet):
try:
router = sdk.network.find_router(name_or_id=subnet.name)
if router is None:
return
router_port = next(sdk.network.ports(
network_id=subnet.network_id,
device_owner='network:router_interface'))
sdk.network.remove_interface_from_router(router.id, subnet.id,
router_port.id)
except StopIteration:
# There is no router port
pass
except Exception:
print('ERROR: Delete router %s failed.' % subnet.name)
raise raise
@ -224,11 +250,20 @@ def _local_neutron_segments_and_subnets(sdk, ctlplane_id, net_cidrs):
"""Create's and updates the ctlplane subnet on the segment that is local to """Create's and updates the ctlplane subnet on the segment that is local to
the underclud. the underclud.
""" """
s = CONF['subnets'][CONF['local_subnet']] s = CONF['subnets'][CONF['local_subnet']]
# If the subnet is IPv6 we need to start a router so that router
# advertisements are sent out for stateless IP addressing to work.
# NOTE(hjensas): Don't do this for routed networks. The router will
# use the address defined as gateway for the subnet, and in a
# deployment with routed networks this will conflict with the router
# in the infrastructure. The router in the infrastructure must be
# configured to send router advertisements.
enable_ipv6_router = (CONF['enable_routed_networks'] is False
and netaddr.IPNetwork(s['NetworkCidr']).version == 6)
name = CONF['local_subnet'] name = CONF['local_subnet']
subnet = _get_subnet(sdk, s['NetworkCidr'], ctlplane_id) subnet = _get_subnet(sdk, s['NetworkCidr'], ctlplane_id)
segment = _get_segment(sdk, CONF['physical_network'], ctlplane_id) segment = _get_segment(sdk, CONF['physical_network'], ctlplane_id)
if subnet: if subnet:
if CONF['enable_routed_networks'] and subnet.segment_id is None: if CONF['enable_routed_networks'] and subnet.segment_id is None:
# The subnet exists and does not have a segment association. Since # The subnet exists and does not have a segment association. Since
@ -237,6 +272,12 @@ def _local_neutron_segments_and_subnets(sdk, ctlplane_id, net_cidrs):
# networks subnet by associating the network segment_id with the # networks subnet by associating the network segment_id with the
# subnet. # subnet.
_neutron_add_subnet_segment_association(sdk, subnet.id, segment.id) _neutron_add_subnet_segment_association(sdk, subnet.id, segment.id)
# The router interface must be removed prior to updating the subnet
# in case the gateway_ip of the subnet changed.
if enable_ipv6_router:
_remove_router_port(sdk, subnet)
_neutron_subnet_update( _neutron_subnet_update(
sdk, subnet.id, s['NetworkCidr'], s['NetworkGateway'], sdk, subnet.id, s['NetworkCidr'], s['NetworkGateway'],
s['HostRoutes'], s.get('AllocationPools'), name, s['HostRoutes'], s.get('AllocationPools'), name,
@ -246,20 +287,16 @@ def _local_neutron_segments_and_subnets(sdk, ctlplane_id, net_cidrs):
segment_id = segment.id segment_id = segment.id
else: else:
segment_id = None segment_id = None
subnet = _neutron_subnet_create( subnet = _neutron_subnet_create(
sdk, ctlplane_id, s['NetworkCidr'], s['NetworkGateway'], sdk, ctlplane_id, s['NetworkCidr'], s['NetworkGateway'],
s['HostRoutes'], s.get('AllocationPools'), name, segment_id, s['HostRoutes'], s.get('AllocationPools'), name, segment_id,
s['DnsNameServers']) s['DnsNameServers'])
# If the subnet is IPv6 we need to start a router so that router
# advertisments are sent out for stateless IP addressing to work. if enable_ipv6_router:
# NOTE(hjensas): Don't do this for routed networks. The router will _ensure_neutron_router(sdk, subnet)
# use the address defined as gateway for the subnet, and in a _add_router_port(sdk, subnet)
# deployment with routed networks this will conflict with the router
# in the infrastructure. The router in the infrastucture must be
# configured to send router advertisements.
if not CONF['enable_routed_networks']:
if netaddr.IPNetwork(s['NetworkCidr']).version == 6:
_ensure_neutron_router(sdk, name, subnet.id)
net_cidrs.append(s['NetworkCidr']) net_cidrs.append(s['NetworkCidr'])
return net_cidrs return net_cidrs