Separate floating IP port creation from transaction

This moves the floating IP port creation outside of the transaction
that creates the floating IP record. This eliminates the use of one
of the GUARD_TRANSACTION flags to prepare us for the enginefacade
switch and correct event notification semantics for push
notifications.

Note that this introduces a small window where the server can die
mid-process and leave an orphaned port without a floating IP. This
is similar to what can happen for router gateway ports. The intention
is to address these in a follow-up patch with a periodic garbage
collection task that will look for floating IP ports with a device
ID of 'PENDING' since the tenant cannot delete them on their own.

Related-Bug: #1540844
Partially-Implements: blueprint push-notifications
Partially-Implements: blueprint enginefacade-switch
Change-Id: Ia4c34c6654a5bfb64fbf06b11b0a29b018c6854f
This commit is contained in:
Kevin Benton 2016-11-09 23:25:01 -08:00 committed by Kevin Benton
parent 03f7ec38b6
commit c9358687ca
2 changed files with 33 additions and 26 deletions

View File

@ -1194,34 +1194,36 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
msg = _("Network %s does not contain any IPv4 subnet") % f_net_id
raise n_exc.BadRequest(resource='floatingip', msg=msg)
with context.session.begin(subtransactions=True):
# This external port is never exposed to the tenant.
# it is used purely for internal system and admin use when
# managing floating IPs.
# This external port is never exposed to the tenant.
# it is used purely for internal system and admin use when
# managing floating IPs.
port = {'tenant_id': '', # tenant intentionally not set
'network_id': f_net_id,
'admin_state_up': True,
'device_id': fip_id,
'device_owner': DEVICE_OWNER_FLOATINGIP,
'status': lib_constants.PORT_STATUS_NOTAPPLICABLE,
'name': ''}
if fip.get('floating_ip_address'):
port['fixed_ips'] = [
{'ip_address': fip['floating_ip_address']}]
port = {'tenant_id': '', # tenant intentionally not set
'network_id': f_net_id,
'admin_state_up': True,
'device_id': 'PENDING',
'device_owner': DEVICE_OWNER_FLOATINGIP,
'status': lib_constants.PORT_STATUS_NOTAPPLICABLE,
'name': ''}
if fip.get('floating_ip_address'):
port['fixed_ips'] = [
{'ip_address': fip['floating_ip_address']}]
if fip.get('subnet_id'):
port['fixed_ips'] = [
{'subnet_id': fip['subnet_id']}]
if fip.get('subnet_id'):
port['fixed_ips'] = [
{'subnet_id': fip['subnet_id']}]
# 'status' in port dict could not be updated by default, use
# check_allow_post to stop the verification of system
# TODO(kevinbenton): move this out of transaction
setattr(context, 'GUARD_TRANSACTION', False)
external_port = p_utils.create_port(self._core_plugin,
context.elevated(),
{'port': port},
check_allow_post=False)
# 'status' in port dict could not be updated by default, use
# check_allow_post to stop the verification of system
external_port = p_utils.create_port(self._core_plugin,
context.elevated(),
{'port': port},
check_allow_post=False)
with p_utils.delete_port_on_error(self._core_plugin,
context.elevated(),
external_port['id']),\
context.session.begin(subtransactions=True):
# Ensure IPv4 addresses are allocated on external port
external_ipv4_ips = self._port_ipv4_fixed_ips(external_port)
if not external_ipv4_ips:
@ -1247,6 +1249,11 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
if self._is_dns_integration_supported:
dns_data = self._process_dns_floatingip_create_precommit(
context, floatingip_dict, fip)
# TODO(kevinbenton): garbage collector that deletes ports that didn't
# make it this far to handle servers dying in this process
self._core_plugin.update_port(context.elevated(), external_port['id'],
{'port': {'device_id': fip_id}})
registry.notify(resources.FLOATING_IP,
events.AFTER_UPDATE,
self._update_fip_assoc,

View File

@ -2531,7 +2531,7 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
None)
with mock.patch(plugin_class + '.create_port') as createport:
createport.return_value = {'fixed_ips': []}
createport.return_value = {'fixed_ips': [], 'id': '44'}
res = self._create_floatingip(
self.fmt, public_sub['subnet']['network_id'],
port_id=p['port']['id'])