Merge "Fix handling of floating IP association info in Nicira plugin"

This commit is contained in:
Jenkins 2013-09-19 21:18:43 +00:00 committed by Gerrit Code Review
commit 90a7fd4353
2 changed files with 120 additions and 50 deletions

View File

@ -1775,16 +1775,6 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
context, context,
fip, fip,
floatingip_db['floating_network_id']) floatingip_db['floating_network_id'])
# Retrieve and delete existing NAT rules, if any
if not router_id and floatingip_db.get('fixed_port_id'):
# This happens if we're disassociating. Need to explicitly
# find the router serving this floating IP
tmp_fip = fip.copy()
tmp_fip['port_id'] = floatingip_db['fixed_port_id']
_pid, internal_ip, router_id = self.get_assoc_data(
context, tmp_fip, floatingip_db['floating_network_id'])
return (port_id, internal_ip, router_id) return (port_id, internal_ip, router_id)
def _update_fip_assoc(self, context, fip, floatingip_db, external_port): def _update_fip_assoc(self, context, fip, floatingip_db, external_port):
@ -1793,6 +1783,8 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
Overrides method from base class. Overrides method from base class.
The method is augmented for creating NAT rules in the process. The method is augmented for creating NAT rules in the process.
""" """
# Store router currently serving the floating IP
old_router_id = floatingip_db.router_id
port_id, internal_ip, router_id = self._get_fip_assoc_data( port_id, internal_ip, router_id = self._get_fip_assoc_data(
context, fip, floatingip_db) context, fip, floatingip_db)
floating_ip = floatingip_db['floating_ip_address'] floating_ip = floatingip_db['floating_ip_address']
@ -1803,14 +1795,31 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
internal_ip, internal_ip,
router_id) router_id)
# Fetch logical port of router's external gateway # Fetch logical port of router's external gateway
# Fetch logical port of router's external gateway
nvp_floating_ips = self._build_ip_address_list(
context.elevated(), external_port['fixed_ips'])
floating_ip = floatingip_db['floating_ip_address']
# Retrieve and delete existing NAT rules, if any
if old_router_id:
# Retrieve the current internal ip
_p, _s, old_internal_ip = self._internal_fip_assoc_data(
context, {'id': floatingip_db.id,
'port_id': floatingip_db.fixed_port_id,
'fixed_ip_address': floatingip_db.fixed_ip_address,
'tenant_id': floatingip_db.tenant_id})
nvp_gw_port_id = nvplib.find_router_gw_port(
context, self.cluster, old_router_id)['uuid']
self._retrieve_and_delete_nat_rules(
context, floating_ip, old_internal_ip, old_router_id)
nvplib.update_lrouter_port_ips(
self.cluster, old_router_id, nvp_gw_port_id,
ips_to_add=[], ips_to_remove=nvp_floating_ips)
if router_id:
nvp_gw_port_id = nvplib.find_router_gw_port( nvp_gw_port_id = nvplib.find_router_gw_port(
context, self.cluster, router_id)['uuid'] context, self.cluster, router_id)['uuid']
nvp_floating_ips = self._build_ip_address_list(
context.elevated(), external_port['fixed_ips'])
LOG.debug(_("Address list for NVP logical router "
"port:%s"), nvp_floating_ips)
# Re-create NAT rules only if a port id is specified # Re-create NAT rules only if a port id is specified
if 'port_id' in fip and fip['port_id']: if fip.get('port_id'):
try: try:
# Create new NAT rules # Create new NAT rules
nvplib.create_lrouter_dnat_rule( nvplib.create_lrouter_dnat_rule(
@ -1847,15 +1856,6 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin,
'internal_ip': internal_ip}) 'internal_ip': internal_ip})
msg = _("Failed to update NAT rules for floatingip update") msg = _("Failed to update NAT rules for floatingip update")
raise nvp_exc.NvpPluginException(err_msg=msg) raise nvp_exc.NvpPluginException(err_msg=msg)
elif floatingip_db['fixed_port_id']:
# This is a disassociation.
# Remove floating IP address from logical router port
internal_ip = None
nvplib.update_lrouter_port_ips(self.cluster,
router_id,
nvp_gw_port_id,
ips_to_add=[],
ips_to_remove=nvp_floating_ips)
floatingip_db.update({'fixed_ip_address': internal_ip, floatingip_db.update({'fixed_ip_address': internal_ip,
'fixed_port_id': port_id, 'fixed_port_id': port_id,

View File

@ -474,35 +474,43 @@ class L3NatTestCaseMixin(object):
r['router']['id'], r['router']['id'],
public_sub['subnet']['network_id']) public_sub['subnet']['network_id'])
@contextlib.contextmanager
def floatingip_no_assoc_with_public_sub(
self, private_sub, fmt=None, set_context=False, public_sub=None):
self._set_net_external(public_sub['subnet']['network_id'])
with self.router() as r:
floatingip = None
try:
self._add_external_gateway_to_router(
r['router']['id'],
public_sub['subnet']['network_id'])
self._router_interface_action('add', r['router']['id'],
private_sub['subnet']['id'],
None)
floatingip = self._make_floatingip(
fmt or self.fmt,
public_sub['subnet']['network_id'],
set_context=set_context)
yield floatingip, r
finally:
if floatingip:
self._delete('floatingips',
floatingip['floatingip']['id'])
self._router_interface_action('remove', r['router']['id'],
private_sub['subnet']['id'],
None)
self._remove_external_gateway_from_router(
r['router']['id'],
public_sub['subnet']['network_id'])
@contextlib.contextmanager @contextlib.contextmanager
def floatingip_no_assoc(self, private_sub, fmt=None, set_context=False): def floatingip_no_assoc(self, private_sub, fmt=None, set_context=False):
with self.subnet(cidr='12.0.0.0/24') as public_sub: with self.subnet(cidr='12.0.0.0/24') as public_sub:
self._set_net_external(public_sub['subnet']['network_id']) with self.floatingip_no_assoc_with_public_sub(
with self.router() as r: private_sub, fmt, set_context, public_sub) as (f, r):
floatingip = None # Yield only the floating ip object
try: yield f
self._add_external_gateway_to_router(
r['router']['id'],
public_sub['subnet']['network_id'])
self._router_interface_action('add', r['router']['id'],
private_sub['subnet']['id'],
None)
floatingip = self._make_floatingip(
fmt or self.fmt,
public_sub['subnet']['network_id'],
set_context=set_context)
yield floatingip
finally:
if floatingip:
self._delete('floatingips',
floatingip['floatingip']['id'])
self._router_interface_action('remove', r['router']['id'],
private_sub['subnet']['id'],
None)
self._remove_external_gateway_from_router(
r['router']['id'],
public_sub['subnet']['network_id'])
class L3NatTestCaseBase(L3NatTestCaseMixin): class L3NatTestCaseBase(L3NatTestCaseMixin):
@ -1243,6 +1251,68 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
self.assertEqual(body['floatingip']['fixed_ip_address'], self.assertEqual(body['floatingip']['fixed_ip_address'],
ip_address) ip_address)
def test_floatingip_update_different_router(self):
# Create subnet with different CIDRs to account for plugins which
# do not support overlapping IPs
with contextlib.nested(self.subnet(cidr='10.0.0.0/24'),
self.subnet(cidr='10.0.1.0/24')) as (
s1, s2):
with contextlib.nested(self.port(subnet=s1),
self.port(subnet=s2)) as (p1, p2):
private_sub1 = {'subnet':
{'id':
p1['port']['fixed_ips'][0]['subnet_id']}}
private_sub2 = {'subnet':
{'id':
p2['port']['fixed_ips'][0]['subnet_id']}}
with self.subnet(cidr='12.0.0.0/24') as public_sub:
with contextlib.nested(
self.floatingip_no_assoc_with_public_sub(
private_sub1, public_sub=public_sub),
self.floatingip_no_assoc_with_public_sub(
private_sub2, public_sub=public_sub)) as (
(fip1, r1), (fip2, r2)):
def assert_no_assoc(fip):
body = self._show('floatingips',
fip['floatingip']['id'])
self.assertEqual(body['floatingip']['port_id'],
None)
self.assertIsNone(
body['floatingip']['fixed_ip_address'], None)
assert_no_assoc(fip1)
assert_no_assoc(fip2)
def associate_and_assert(fip, port):
port_id = port['port']['id']
ip_address = (port['port']['fixed_ips']
[0]['ip_address'])
body = self._update(
'floatingips', fip['floatingip']['id'],
{'floatingip': {'port_id': port_id}})
self.assertEqual(body['floatingip']['port_id'],
port_id)
self.assertEqual(
body['floatingip']['fixed_ip_address'],
ip_address)
return body['floatingip']['router_id']
fip1_r1_res = associate_and_assert(fip1, p1)
self.assertEqual(fip1_r1_res, r1['router']['id'])
# The following operation will associate the floating
# ip to a different router
fip1_r2_res = associate_and_assert(fip1, p2)
self.assertEqual(fip1_r2_res, r2['router']['id'])
fip2_r1_res = associate_and_assert(fip2, p1)
self.assertEqual(fip2_r1_res, r1['router']['id'])
# disassociate fip1
self._update(
'floatingips', fip1['floatingip']['id'],
{'floatingip': {'port_id': None}})
fip2_r2_res = associate_and_assert(fip2, p2)
self.assertEqual(fip2_r2_res, r2['router']['id'])
def test_floatingip_with_assoc(self): def test_floatingip_with_assoc(self):
with self.floatingip_with_assoc() as fip: with self.floatingip_with_assoc() as fip:
body = self._show('floatingips', fip['floatingip']['id']) body = self._show('floatingips', fip['floatingip']['id'])