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.
|
# under the License.
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
|
import netaddr
|
||||||
|
|
||||||
from oslo_db import exception as oslo_db_exc
|
from oslo_db import exception as oslo_db_exc
|
||||||
from oslo_utils import uuidutils
|
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 common_db_mixin as common_db
|
||||||
from neutron.db import l3_attrs_db
|
from neutron.db import l3_attrs_db
|
||||||
from neutron.db import l3_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.models import address_scope as address_scope_db
|
||||||
from neutron.db import models_v2
|
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.plugins.ml2 import models as ml2_models
|
||||||
|
|
||||||
from neutron_dynamic_routing._i18n import _
|
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 the list of host routes given a list of (IP, nexthop)"""
|
||||||
return ({'destination': x + '/32',
|
return ({'destination': x + '/32',
|
||||||
'next_hop': y} for x, y in ip_next_hop_tuples)
|
'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()
|
ctx = context.get_admin_context()
|
||||||
new_router_id = kwargs['router_id']
|
new_router_id = kwargs['router_id']
|
||||||
last_router_id = kwargs['last_known_router_id']
|
last_router_id = kwargs['last_known_router_id']
|
||||||
next_hop = kwargs['next_hop']
|
floating_ip_address = kwargs['floating_ip_address']
|
||||||
dest = kwargs['floating_ip_address'] + '/32'
|
dest = floating_ip_address + '/32'
|
||||||
bgp_speakers = self._bgp_speakers_for_gw_network_by_family(
|
bgp_speakers = self._bgp_speakers_for_gw_network_by_family(
|
||||||
ctx,
|
ctx,
|
||||||
kwargs['floating_network_id'],
|
kwargs['floating_network_id'],
|
||||||
@ -235,7 +235,9 @@ class BgpPlugin(service_base.ServicePluginBase,
|
|||||||
self.stop_route_advertisements(ctx, self._bgp_rpc,
|
self.stop_route_advertisements(ctx, self._bgp_rpc,
|
||||||
bgp_speaker.id, [dest])
|
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}
|
new_host_route = {'destination': dest, 'next_hop': next_hop}
|
||||||
for bgp_speaker in bgp_speakers:
|
for bgp_speaker in bgp_speakers:
|
||||||
self.start_route_advertisements(ctx, self._bgp_rpc,
|
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):
|
def test_ha_router_fips_has_no_next_hop_to_fip_agent_gateway(self):
|
||||||
self._test_legacy_router_fips_next_hop(router_ha=True)
|
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