Let the bgp_plugin to query floating IP bgp next_hop
When a dvr floating IP is associated to a port, the bgp plugin `floatingip_update_callback` will immediately send a notification `start_route_advertisements` to DR agent, there is a new floating IP bgp route. But this bgp route is not correct, its next_hop is set to dvr router snat gateway IP address. And then after `periodic_interval` seconds, the DR agent will resync that DVR fip route with new next_hop to the correct FIP namespace fg-device IP address. This patch will let the bgp_plugin to handle the floating IP bgp route next_hop query. Change-Id: Ic6bb7f4263c6e2da315178be2ed041eb7020c905 Closes-bug: #1615919
This commit is contained in:
parent
130861b86b
commit
0980985b2f
@ -13,6 +13,7 @@
|
||||
# under the License.
|
||||
|
||||
import itertools
|
||||
import netaddr
|
||||
|
||||
from oslo_db import exception as oslo_db_exc
|
||||
from oslo_utils import uuidutils
|
||||
@ -30,8 +31,10 @@ from neutron_lib import exceptions as n_exc
|
||||
from neutron.db import common_db_mixin as common_db
|
||||
from neutron.db import l3_attrs_db
|
||||
from neutron.db import l3_db
|
||||
from neutron.db import l3_dvr_db
|
||||
from neutron.db.models import address_scope as address_scope_db
|
||||
from neutron.db import models_v2
|
||||
from neutron.extensions import l3 as l3_ext
|
||||
from neutron.plugins.ml2 import models as ml2_models
|
||||
|
||||
from neutron_dynamic_routing._i18n import _
|
||||
@ -1016,3 +1019,66 @@ class BgpDbMixin(common_db.CommonDbMixin):
|
||||
"""Return the list of host routes given a list of (IP, nexthop)"""
|
||||
return ({'destination': x + '/32',
|
||||
'next_hop': y} for x, y in ip_next_hop_tuples)
|
||||
|
||||
def _get_router(self, context, router_id):
|
||||
try:
|
||||
router = self._get_by_id(context, l3_db.Router, router_id)
|
||||
except sa_exc.NoResultFound:
|
||||
raise l3_ext.RouterNotFound(router_id=router_id)
|
||||
return router
|
||||
|
||||
def _get_fip_next_hop(self, context, router_id, ip_address=None):
|
||||
router = self._get_router(context, router_id)
|
||||
gw_port = router.gw_port
|
||||
if not gw_port:
|
||||
return
|
||||
|
||||
if l3_dvr_db.is_distributed_router(router) and ip_address:
|
||||
return self._get_dvr_fip_next_hop(context, ip_address)
|
||||
|
||||
for fixed_ip in gw_port.fixed_ips:
|
||||
addr = netaddr.IPAddress(fixed_ip.ip_address)
|
||||
if addr.version == 4:
|
||||
return fixed_ip.ip_address
|
||||
|
||||
def _get_dvr_fip_agent_gateway_query(self, context):
|
||||
ML2PortBinding = ml2_models.PortBinding
|
||||
IpAllocation = models_v2.IPAllocation
|
||||
Port = models_v2.Port
|
||||
base_query = context.session.query(Port.network_id,
|
||||
ML2PortBinding.host,
|
||||
IpAllocation.ip_address)
|
||||
|
||||
gw_query = base_query.filter(
|
||||
ML2PortBinding.port_id == Port.id,
|
||||
IpAllocation.port_id == Port.id,
|
||||
Port.device_owner == lib_consts.DEVICE_OWNER_AGENT_GW)
|
||||
return gw_query
|
||||
|
||||
def _get_fip_fixed_port_host_query(self, context, fip_address):
|
||||
ML2PortBinding = ml2_models.PortBinding
|
||||
|
||||
fip_query = context.session.query(
|
||||
l3_db.FloatingIP.floating_network_id,
|
||||
ML2PortBinding.host,
|
||||
l3_db.FloatingIP.floating_ip_address)
|
||||
fip_query = fip_query.filter(
|
||||
l3_db.FloatingIP.fixed_port_id == ML2PortBinding.port_id,
|
||||
l3_db.FloatingIP.floating_ip_address == fip_address)
|
||||
return fip_query
|
||||
|
||||
def _get_dvr_fip_next_hop(self, context, fip_address):
|
||||
try:
|
||||
dvr_agent_gw_query = self._get_dvr_fip_agent_gateway_query(
|
||||
context)
|
||||
fip_fix_port_query = self._get_fip_fixed_port_host_query(
|
||||
context, fip_address)
|
||||
q = self._join_fip_by_host_binding_to_agent_gateway(
|
||||
context,
|
||||
fip_fix_port_query.subquery(),
|
||||
dvr_agent_gw_query.subquery()).one()
|
||||
return q[1]
|
||||
except sa_exc.NoResultFound:
|
||||
return
|
||||
except sa_exc.MultipleResultsFound:
|
||||
return
|
||||
|
@ -223,8 +223,8 @@ class BgpPlugin(service_base.ServicePluginBase,
|
||||
ctx = context.get_admin_context()
|
||||
new_router_id = kwargs['router_id']
|
||||
last_router_id = kwargs['last_known_router_id']
|
||||
next_hop = kwargs['next_hop']
|
||||
dest = kwargs['floating_ip_address'] + '/32'
|
||||
floating_ip_address = kwargs['floating_ip_address']
|
||||
dest = floating_ip_address + '/32'
|
||||
bgp_speakers = self._bgp_speakers_for_gw_network_by_family(
|
||||
ctx,
|
||||
kwargs['floating_network_id'],
|
||||
@ -235,7 +235,9 @@ class BgpPlugin(service_base.ServicePluginBase,
|
||||
self.stop_route_advertisements(ctx, self._bgp_rpc,
|
||||
bgp_speaker.id, [dest])
|
||||
|
||||
if next_hop and new_router_id != last_router_id:
|
||||
if new_router_id and new_router_id != last_router_id:
|
||||
next_hop = self._get_fip_next_hop(
|
||||
ctx, new_router_id, floating_ip_address)
|
||||
new_host_route = {'destination': dest, 'next_hop': next_hop}
|
||||
for bgp_speaker in bgp_speakers:
|
||||
self.start_route_advertisements(ctx, self._bgp_rpc,
|
||||
|
@ -1213,3 +1213,48 @@ class BgpTests(test_plugin.Ml2PluginV2TestCase,
|
||||
|
||||
def test_ha_router_fips_has_no_next_hop_to_fip_agent_gateway(self):
|
||||
self._test_legacy_router_fips_next_hop(router_ha=True)
|
||||
|
||||
def _test__get_fip_next_hop(self, distributed=False):
|
||||
gw_prefix = '172.16.10.0/24'
|
||||
tenant_prefix = '10.10.10.0/24'
|
||||
tenant_id = _uuid()
|
||||
agent_confs = [{"host": "compute1", "mode": "dvr"},
|
||||
{"host": "network1", "mode": "dvr_snat"}]
|
||||
self._create_scenario_test_l3_agents(agent_confs)
|
||||
with self.router_with_external_and_tenant_networks(
|
||||
tenant_id=tenant_id,
|
||||
gw_prefix=gw_prefix,
|
||||
tenant_prefix=tenant_prefix,
|
||||
distributed=distributed) as res:
|
||||
router, ext_net, int_net = res
|
||||
ext_gw_info = router['external_gateway_info']
|
||||
legacy_gw_ip = ext_gw_info[
|
||||
'external_fixed_ips'][0]['ip_address']
|
||||
gw_net_id = ext_net['network']['id']
|
||||
# Whatever the router type is, we always create such dvr fip agent
|
||||
# gateway port, in order to make sure that legacy router fip bgp
|
||||
# route next_hop is not the dvr_fip_agent_gw.
|
||||
fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists(
|
||||
self.context, gw_net_id, 'compute1')
|
||||
port_configs = [{'net_id': int_net['network']['id'],
|
||||
'host': 'compute1'}]
|
||||
ports = self._create_scenario_test_ports(tenant_id, port_configs)
|
||||
dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address']
|
||||
fip_data = {'floatingip': {'floating_network_id': gw_net_id,
|
||||
'tenant_id': tenant_id,
|
||||
'port_id': ports[0]['id']}}
|
||||
fip = self.l3plugin.create_floatingip(self.context, fip_data)
|
||||
fip_address = fip['floating_ip_address']
|
||||
with self.bgp_speaker(4, 1234, networks=[gw_net_id]):
|
||||
next_hop = self.bgp_plugin._get_fip_next_hop(
|
||||
self.context, router['id'], fip_address)
|
||||
if distributed:
|
||||
self.assertEqual(dvr_gw_ip, next_hop)
|
||||
else:
|
||||
self.assertEqual(legacy_gw_ip, next_hop)
|
||||
|
||||
def test__get_fip_next_hop_legacy(self):
|
||||
self._test__get_fip_next_hop()
|
||||
|
||||
def test__get_fip_next_hop_dvr(self):
|
||||
self._test__get_fip_next_hop(distributed=True)
|
||||
|
Loading…
Reference in New Issue
Block a user