Queries for DVR-aware floating IP next-hop lookups
Introduce calls to collect host routes for floating IP's in a DVR-aware way. When a floating IP is associated through a distributed router, the next-hop for the floating IP is announced as the floating IP agent gateway port on the corresponding host. Change-Id: I654e4d2496e19c011653eedb87c9b6026f801e9f Implements: blueprint bgp-dynamic-routing DocImpact: Neutron BGP should have docs created for the feature
This commit is contained in:
parent
f0b042f58a
commit
8a9d83dc0b
@ -34,6 +34,7 @@ from neutron.db import l3_db
|
|||||||
from neutron.db import model_base
|
from neutron.db import model_base
|
||||||
from neutron.db import models_v2
|
from neutron.db import models_v2
|
||||||
from neutron.extensions import bgp as bgp_ext
|
from neutron.extensions import bgp as bgp_ext
|
||||||
|
from neutron.plugins.ml2 import models as ml2_models
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -464,7 +465,10 @@ class BgpDbMixin(common_db.CommonDbMixin):
|
|||||||
fip_routes = self._get_central_fip_host_routes_by_bgp_speaker(
|
fip_routes = self._get_central_fip_host_routes_by_bgp_speaker(
|
||||||
context,
|
context,
|
||||||
bgp_speaker_id)
|
bgp_speaker_id)
|
||||||
return itertools.chain(fip_routes, net_routes)
|
dvr_fip_routes = self._get_dvr_fip_host_routes_by_bgp_speaker(
|
||||||
|
context,
|
||||||
|
bgp_speaker_id)
|
||||||
|
return itertools.chain(fip_routes, net_routes, dvr_fip_routes)
|
||||||
|
|
||||||
def get_routes_by_bgp_speaker_binding(self, context,
|
def get_routes_by_bgp_speaker_binding(self, context,
|
||||||
bgp_speaker_id, network_id):
|
bgp_speaker_id, network_id):
|
||||||
@ -478,7 +482,11 @@ class BgpDbMixin(common_db.CommonDbMixin):
|
|||||||
context,
|
context,
|
||||||
network_id,
|
network_id,
|
||||||
bgp_speaker_id)
|
bgp_speaker_id)
|
||||||
return itertools.chain(fip_routes, net_routes)
|
dvr_fip_routes = self._get_dvr_fip_host_routes_by_binding(
|
||||||
|
context,
|
||||||
|
network_id,
|
||||||
|
bgp_speaker_id)
|
||||||
|
return itertools.chain(fip_routes, net_routes, dvr_fip_routes)
|
||||||
|
|
||||||
def _get_routes_by_router(self, context, router_id):
|
def _get_routes_by_router(self, context, router_id):
|
||||||
bgp_speaker_ids = self._get_bgp_speaker_ids_by_router(context,
|
bgp_speaker_ids = self._get_bgp_speaker_ids_by_router(context,
|
||||||
@ -493,8 +501,12 @@ class BgpDbMixin(common_db.CommonDbMixin):
|
|||||||
context,
|
context,
|
||||||
router_id,
|
router_id,
|
||||||
bgp_speaker_id)
|
bgp_speaker_id)
|
||||||
routes = list(itertools.chain(fip_routes, net_routes))
|
dvr_fip_routes = self._get_dvr_fip_host_routes_by_router(
|
||||||
route_dict[bgp_speaker_id] = routes
|
context,
|
||||||
|
router_id,
|
||||||
|
bgp_speaker_id)
|
||||||
|
routes = itertools.chain(fip_routes, net_routes, dvr_fip_routes)
|
||||||
|
route_dict[bgp_speaker_id] = list(routes)
|
||||||
return route_dict
|
return route_dict
|
||||||
|
|
||||||
def _get_central_fip_host_routes_by_router(self, context, router_id,
|
def _get_central_fip_host_routes_by_router(self, context, router_id,
|
||||||
@ -533,6 +545,21 @@ class BgpDbMixin(common_db.CommonDbMixin):
|
|||||||
query = query.filter(router_attrs.distributed != sa.sql.true())
|
query = query.filter(router_attrs.distributed != sa.sql.true())
|
||||||
return self._host_route_list_from_tuples(query.all())
|
return self._host_route_list_from_tuples(query.all())
|
||||||
|
|
||||||
|
def _get_dvr_fip_host_routes_by_router(self, context, bgp_speaker_id,
|
||||||
|
router_id):
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
gw_query = self._get_gateway_query(context, bgp_speaker_id)
|
||||||
|
|
||||||
|
fip_query = self._get_fip_query(context, bgp_speaker_id)
|
||||||
|
fip_query.filter(l3_db.FloatingIP.router_id == router_id)
|
||||||
|
|
||||||
|
#Create the join query
|
||||||
|
join_query = self._join_fip_by_host_binding_to_agent_gateway(
|
||||||
|
context,
|
||||||
|
fip_query.subquery(),
|
||||||
|
gw_query.subquery())
|
||||||
|
return self._host_route_list_from_tuples(join_query.all())
|
||||||
|
|
||||||
def _get_central_fip_host_routes_by_binding(self, context,
|
def _get_central_fip_host_routes_by_binding(self, context,
|
||||||
network_id, bgp_speaker_id):
|
network_id, bgp_speaker_id):
|
||||||
"""Get all floating IP host routes for the given network binding."""
|
"""Get all floating IP host routes for the given network binding."""
|
||||||
@ -572,6 +599,24 @@ class BgpDbMixin(common_db.CommonDbMixin):
|
|||||||
query = query.filter(router_attrs.distributed != sa.sql.true())
|
query = query.filter(router_attrs.distributed != sa.sql.true())
|
||||||
return self._host_route_list_from_tuples(query.all())
|
return self._host_route_list_from_tuples(query.all())
|
||||||
|
|
||||||
|
def _get_dvr_fip_host_routes_by_binding(self, context, network_id,
|
||||||
|
bgp_speaker_id):
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
BgpBinding = BgpSpeakerNetworkBinding
|
||||||
|
|
||||||
|
gw_query = self._get_gateway_query(context, bgp_speaker_id)
|
||||||
|
gw_query.filter(BgpBinding.network_id == network_id)
|
||||||
|
|
||||||
|
fip_query = self._get_fip_query(context, bgp_speaker_id)
|
||||||
|
fip_query.filter(BgpBinding.network_id == network_id)
|
||||||
|
|
||||||
|
#Create the join query
|
||||||
|
join_query = self._join_fip_by_host_binding_to_agent_gateway(
|
||||||
|
context,
|
||||||
|
fip_query.subquery(),
|
||||||
|
gw_query.subquery())
|
||||||
|
return self._host_route_list_from_tuples(join_query.all())
|
||||||
|
|
||||||
def _get_central_fip_host_routes_by_bgp_speaker(self, context,
|
def _get_central_fip_host_routes_by_bgp_speaker(self, context,
|
||||||
bgp_speaker_id):
|
bgp_speaker_id):
|
||||||
"""Get all the floating IP host routes advertised by a BgpSpeaker."""
|
"""Get all the floating IP host routes advertised by a BgpSpeaker."""
|
||||||
@ -613,6 +658,65 @@ class BgpDbMixin(common_db.CommonDbMixin):
|
|||||||
query = query.filter(router_attrs.distributed != sa.sql.true())
|
query = query.filter(router_attrs.distributed != sa.sql.true())
|
||||||
return self._host_route_list_from_tuples(query.all())
|
return self._host_route_list_from_tuples(query.all())
|
||||||
|
|
||||||
|
def _get_gateway_query(self, context, bgp_speaker_id):
|
||||||
|
BgpBinding = BgpSpeakerNetworkBinding
|
||||||
|
ML2PortBinding = ml2_models.PortBinding
|
||||||
|
IpAllocation = models_v2.IPAllocation
|
||||||
|
Port = models_v2.Port
|
||||||
|
gw_query = context.session.query(Port.network_id,
|
||||||
|
ML2PortBinding.host,
|
||||||
|
IpAllocation.ip_address)
|
||||||
|
|
||||||
|
#Subquery for FIP agent gateway ports
|
||||||
|
gw_query = gw_query.filter(
|
||||||
|
ML2PortBinding.port_id == Port.id,
|
||||||
|
IpAllocation.port_id == Port.id,
|
||||||
|
IpAllocation.subnet_id == models_v2.Subnet.id,
|
||||||
|
models_v2.Subnet.ip_version == 4,
|
||||||
|
Port.device_owner == lib_consts.DEVICE_OWNER_AGENT_GW,
|
||||||
|
Port.network_id == BgpBinding.network_id,
|
||||||
|
BgpBinding.bgp_speaker_id == bgp_speaker_id,
|
||||||
|
BgpBinding.ip_version == 4)
|
||||||
|
return gw_query
|
||||||
|
|
||||||
|
def _get_fip_query(self, context, bgp_speaker_id):
|
||||||
|
BgpBinding = BgpSpeakerNetworkBinding
|
||||||
|
ML2PortBinding = ml2_models.PortBinding
|
||||||
|
|
||||||
|
#Subquery for floating IP's
|
||||||
|
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_network_id == BgpBinding.network_id,
|
||||||
|
BgpBinding.bgp_speaker_id == bgp_speaker_id)
|
||||||
|
return fip_query
|
||||||
|
|
||||||
|
def _get_dvr_fip_host_routes_by_bgp_speaker(self, context,
|
||||||
|
bgp_speaker_id):
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
gw_query = self._get_gateway_query(context, bgp_speaker_id)
|
||||||
|
fip_query = self._get_fip_query(context, bgp_speaker_id)
|
||||||
|
|
||||||
|
#Create the join query
|
||||||
|
join_query = self._join_fip_by_host_binding_to_agent_gateway(
|
||||||
|
context,
|
||||||
|
fip_query.subquery(),
|
||||||
|
gw_query.subquery())
|
||||||
|
return self._host_route_list_from_tuples(join_query.all())
|
||||||
|
|
||||||
|
def _join_fip_by_host_binding_to_agent_gateway(self, context,
|
||||||
|
fip_subq, gw_subq):
|
||||||
|
join_query = context.session.query(fip_subq.c.floating_ip_address,
|
||||||
|
gw_subq.c.ip_address)
|
||||||
|
and_cond = and_(
|
||||||
|
gw_subq.c.host == fip_subq.c.host,
|
||||||
|
gw_subq.c.network_id == fip_subq.c.floating_network_id)
|
||||||
|
|
||||||
|
return join_query.join(gw_subq, and_cond)
|
||||||
|
|
||||||
def _get_tenant_network_routes_by_binding(self, context,
|
def _get_tenant_network_routes_by_binding(self, context,
|
||||||
network_id, bgp_speaker_id):
|
network_id, bgp_speaker_id):
|
||||||
"""Get all tenant network routes for the given network."""
|
"""Get all tenant network routes for the given network."""
|
||||||
|
@ -20,6 +20,7 @@ from neutron.api.v2 import attributes as attrs
|
|||||||
from neutron.common import exceptions as n_exc
|
from neutron.common import exceptions as n_exc
|
||||||
from neutron.extensions import bgp
|
from neutron.extensions import bgp
|
||||||
from neutron.extensions import external_net
|
from neutron.extensions import external_net
|
||||||
|
from neutron.extensions import portbindings
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
from neutron.plugins.common import constants as p_const
|
from neutron.plugins.common import constants as p_const
|
||||||
from neutron.services.bgp import bgp_plugin
|
from neutron.services.bgp import bgp_plugin
|
||||||
@ -145,6 +146,11 @@ class BgpTests(test_plugin.Ml2PluginV2TestCase,
|
|||||||
BgpEntityCreationMixin):
|
BgpEntityCreationMixin):
|
||||||
fmt = 'json'
|
fmt = 'json'
|
||||||
|
|
||||||
|
def setup_parent(self):
|
||||||
|
self.l3_plugin = ('neutron.tests.unit.extensions.test_l3.'
|
||||||
|
'TestL3NatAgentSchedulingServicePlugin')
|
||||||
|
super(BgpTests, self).setup_parent()
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(BgpTests, self).setUp()
|
super(BgpTests, self).setUp()
|
||||||
self.l3plugin = manager.NeutronManager.get_service_plugins().get(
|
self.l3plugin = manager.NeutronManager.get_service_plugins().get(
|
||||||
@ -775,6 +781,184 @@ class BgpTests(test_plugin.Ml2PluginV2TestCase,
|
|||||||
self.assertTrue(tenant_prefix_found)
|
self.assertTrue(tenant_prefix_found)
|
||||||
self.assertTrue(fip_prefix_found)
|
self.assertTrue(fip_prefix_found)
|
||||||
|
|
||||||
|
def test_get_routes_by_bgp_speaker_id_with_fip_dvr(self):
|
||||||
|
gw_prefix = '172.16.10.0/24'
|
||||||
|
tenant_prefix = '10.10.10.0/24'
|
||||||
|
tenant_id = _uuid()
|
||||||
|
scope_data = {'tenant_id': tenant_id, 'ip_version': 4,
|
||||||
|
'shared': True, 'name': 'bgp-scope'}
|
||||||
|
scope = self.plugin.create_address_scope(
|
||||||
|
self.context,
|
||||||
|
{'address_scope': scope_data})
|
||||||
|
with self.router_with_external_and_tenant_networks(
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
gw_prefix=gw_prefix,
|
||||||
|
tenant_prefix=tenant_prefix,
|
||||||
|
address_scope=scope,
|
||||||
|
distributed=True) as res:
|
||||||
|
router, ext_net, int_net = res
|
||||||
|
ext_gw_info = router['external_gateway_info']
|
||||||
|
gw_net_id = ext_net['network']['id']
|
||||||
|
tenant_net_id = int_net['network']['id']
|
||||||
|
fixed_port_data = {'port':
|
||||||
|
{'name': 'test',
|
||||||
|
'network_id': tenant_net_id,
|
||||||
|
'tenant_id': tenant_id,
|
||||||
|
'admin_state_up': True,
|
||||||
|
'device_id': _uuid(),
|
||||||
|
'device_owner': 'compute:nova',
|
||||||
|
'mac_address': attrs.ATTR_NOT_SPECIFIED,
|
||||||
|
'fixed_ips': attrs.ATTR_NOT_SPECIFIED,
|
||||||
|
portbindings.HOST_ID: 'test-host'}}
|
||||||
|
fixed_port = self.plugin.create_port(self.context,
|
||||||
|
fixed_port_data)
|
||||||
|
self.plugin._create_or_update_agent(self.context,
|
||||||
|
{'agent_type': 'L3 agent',
|
||||||
|
'host': 'test-host',
|
||||||
|
'binary': 'neutron-l3-agent',
|
||||||
|
'topic': 'test'})
|
||||||
|
fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists(
|
||||||
|
self.context,
|
||||||
|
gw_net_id,
|
||||||
|
'test-host')
|
||||||
|
fip_data = {'floatingip': {'floating_network_id': gw_net_id,
|
||||||
|
'tenant_id': tenant_id,
|
||||||
|
'port_id': fixed_port['id']}}
|
||||||
|
fip = self.l3plugin.create_floatingip(self.context, fip_data)
|
||||||
|
fip_prefix = fip['floating_ip_address'] + '/32'
|
||||||
|
with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker:
|
||||||
|
bgp_speaker_id = speaker['id']
|
||||||
|
routes = self.bgp_plugin.get_routes_by_bgp_speaker_id(
|
||||||
|
self.context,
|
||||||
|
bgp_speaker_id)
|
||||||
|
routes = list(routes)
|
||||||
|
cvr_gw_ip = ext_gw_info['external_fixed_ips'][0]['ip_address']
|
||||||
|
dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address']
|
||||||
|
self.assertEqual(2, len(routes))
|
||||||
|
tenant_route_verified = False
|
||||||
|
fip_route_verified = False
|
||||||
|
for route in routes:
|
||||||
|
if route['destination'] == tenant_prefix:
|
||||||
|
self.assertEqual(cvr_gw_ip, route['next_hop'])
|
||||||
|
tenant_route_verified = True
|
||||||
|
if route['destination'] == fip_prefix:
|
||||||
|
self.assertEqual(dvr_gw_ip, route['next_hop'])
|
||||||
|
fip_route_verified = True
|
||||||
|
self.assertTrue(tenant_route_verified)
|
||||||
|
self.assertTrue(fip_route_verified)
|
||||||
|
|
||||||
|
def test__get_dvr_fip_host_routes_by_binding(self):
|
||||||
|
gw_prefix = '172.16.10.0/24'
|
||||||
|
tenant_prefix = '10.10.10.0/24'
|
||||||
|
tenant_id = _uuid()
|
||||||
|
scope_data = {'tenant_id': tenant_id, 'ip_version': 4,
|
||||||
|
'shared': True, 'name': 'bgp-scope'}
|
||||||
|
scope = self.plugin.create_address_scope(
|
||||||
|
self.context,
|
||||||
|
{'address_scope': scope_data})
|
||||||
|
with self.router_with_external_and_tenant_networks(
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
gw_prefix=gw_prefix,
|
||||||
|
tenant_prefix=tenant_prefix,
|
||||||
|
address_scope=scope,
|
||||||
|
distributed=True) as res:
|
||||||
|
router, ext_net, int_net = res
|
||||||
|
gw_net_id = ext_net['network']['id']
|
||||||
|
tenant_net_id = int_net['network']['id']
|
||||||
|
fixed_port_data = {'port':
|
||||||
|
{'name': 'test',
|
||||||
|
'network_id': tenant_net_id,
|
||||||
|
'tenant_id': tenant_id,
|
||||||
|
'admin_state_up': True,
|
||||||
|
'device_id': _uuid(),
|
||||||
|
'device_owner': 'compute:nova',
|
||||||
|
'mac_address': attrs.ATTR_NOT_SPECIFIED,
|
||||||
|
'fixed_ips': attrs.ATTR_NOT_SPECIFIED,
|
||||||
|
portbindings.HOST_ID: 'test-host'}}
|
||||||
|
fixed_port = self.plugin.create_port(self.context,
|
||||||
|
fixed_port_data)
|
||||||
|
self.plugin._create_or_update_agent(self.context,
|
||||||
|
{'agent_type': 'L3 agent',
|
||||||
|
'host': 'test-host',
|
||||||
|
'binary': 'neutron-l3-agent',
|
||||||
|
'topic': 'test'})
|
||||||
|
fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists(
|
||||||
|
self.context,
|
||||||
|
gw_net_id,
|
||||||
|
'test-host')
|
||||||
|
fip_data = {'floatingip': {'floating_network_id': gw_net_id,
|
||||||
|
'tenant_id': tenant_id,
|
||||||
|
'port_id': fixed_port['id']}}
|
||||||
|
fip = self.l3plugin.create_floatingip(self.context, fip_data)
|
||||||
|
fip_prefix = fip['floating_ip_address'] + '/32'
|
||||||
|
with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker:
|
||||||
|
bgp_speaker_id = speaker['id']
|
||||||
|
routes = self.bgp_plugin._get_dvr_fip_host_routes_by_binding(
|
||||||
|
self.context,
|
||||||
|
gw_net_id,
|
||||||
|
bgp_speaker_id)
|
||||||
|
routes = list(routes)
|
||||||
|
dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address']
|
||||||
|
self.assertEqual(1, len(routes))
|
||||||
|
self.assertEqual(dvr_gw_ip, routes[0]['next_hop'])
|
||||||
|
self.assertEqual(fip_prefix, routes[0]['destination'])
|
||||||
|
|
||||||
|
def test__get_dvr_fip_host_routes_by_router(self):
|
||||||
|
gw_prefix = '172.16.10.0/24'
|
||||||
|
tenant_prefix = '10.10.10.0/24'
|
||||||
|
tenant_id = _uuid()
|
||||||
|
scope_data = {'tenant_id': tenant_id, 'ip_version': 4,
|
||||||
|
'shared': True, 'name': 'bgp-scope'}
|
||||||
|
scope = self.plugin.create_address_scope(
|
||||||
|
self.context,
|
||||||
|
{'address_scope': scope_data})
|
||||||
|
with self.router_with_external_and_tenant_networks(
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
gw_prefix=gw_prefix,
|
||||||
|
tenant_prefix=tenant_prefix,
|
||||||
|
address_scope=scope,
|
||||||
|
distributed=True) as res:
|
||||||
|
router, ext_net, int_net = res
|
||||||
|
gw_net_id = ext_net['network']['id']
|
||||||
|
tenant_net_id = int_net['network']['id']
|
||||||
|
fixed_port_data = {'port':
|
||||||
|
{'name': 'test',
|
||||||
|
'network_id': tenant_net_id,
|
||||||
|
'tenant_id': tenant_id,
|
||||||
|
'admin_state_up': True,
|
||||||
|
'device_id': _uuid(),
|
||||||
|
'device_owner': 'compute:nova',
|
||||||
|
'mac_address': attrs.ATTR_NOT_SPECIFIED,
|
||||||
|
'fixed_ips': attrs.ATTR_NOT_SPECIFIED,
|
||||||
|
portbindings.HOST_ID: 'test-host'}}
|
||||||
|
fixed_port = self.plugin.create_port(self.context,
|
||||||
|
fixed_port_data)
|
||||||
|
self.plugin._create_or_update_agent(self.context,
|
||||||
|
{'agent_type': 'L3 agent',
|
||||||
|
'host': 'test-host',
|
||||||
|
'binary': 'neutron-l3-agent',
|
||||||
|
'topic': 'test'})
|
||||||
|
fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists(
|
||||||
|
self.context,
|
||||||
|
gw_net_id,
|
||||||
|
'test-host')
|
||||||
|
fip_data = {'floatingip': {'floating_network_id': gw_net_id,
|
||||||
|
'tenant_id': tenant_id,
|
||||||
|
'port_id': fixed_port['id']}}
|
||||||
|
fip = self.l3plugin.create_floatingip(self.context, fip_data)
|
||||||
|
fip_prefix = fip['floating_ip_address'] + '/32'
|
||||||
|
with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker:
|
||||||
|
bgp_speaker_id = speaker['id']
|
||||||
|
routes = self.bgp_plugin._get_dvr_fip_host_routes_by_router(
|
||||||
|
self.context,
|
||||||
|
bgp_speaker_id,
|
||||||
|
router['id'])
|
||||||
|
routes = list(routes)
|
||||||
|
dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address']
|
||||||
|
self.assertEqual(1, len(routes))
|
||||||
|
self.assertEqual(dvr_gw_ip, routes[0]['next_hop'])
|
||||||
|
self.assertEqual(fip_prefix, routes[0]['destination'])
|
||||||
|
|
||||||
def test_get_routes_by_bgp_speaker_binding_with_fip(self):
|
def test_get_routes_by_bgp_speaker_binding_with_fip(self):
|
||||||
gw_prefix = '172.16.10.0/24'
|
gw_prefix = '172.16.10.0/24'
|
||||||
tenant_prefix = '10.10.10.0/24'
|
tenant_prefix = '10.10.10.0/24'
|
||||||
|
23
releasenotes/notes/bgp-support-ef361825ca63f28b.yaml
Normal file
23
releasenotes/notes/bgp-support-ef361825ca63f28b.yaml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
prelude: >
|
||||||
|
Announcement of tenant prefixes and host routes for floating
|
||||||
|
IP's via BGP is supported
|
||||||
|
features:
|
||||||
|
- Announcement of tenant subnets via BGP using centralized Neutron
|
||||||
|
router gateway port as the next-hop
|
||||||
|
- Announcement of floating IP host routes via BGP using the centralized
|
||||||
|
Neutron router gateway port as the next-hop
|
||||||
|
- Announcement of floating IP host routes via BGP using the floating
|
||||||
|
IP agent gateway as the next-hop when the floating IP is associated
|
||||||
|
through a distributed router
|
||||||
|
issues:
|
||||||
|
- When using DVR, if a floating IP is associated to a fixed IP direct
|
||||||
|
access to the fixed IP is not possible when traffic is sent from
|
||||||
|
outside of a Neutron tenant network (north-south traffic). Traffic
|
||||||
|
sent between tenant networks (east-west traffic) is not affected.
|
||||||
|
When using a distributed router, the floating IP will mask the fixed
|
||||||
|
IP making it inaccessible, even though the tenant subnet is being
|
||||||
|
announced as accessible through the centralized SNAT router. In such
|
||||||
|
a case, traffic sent to the instance should be directed to the
|
||||||
|
floating IP. This is a limitation of the Neutron L3 agent when using
|
||||||
|
DVR and will be addressed in a future release.
|
Loading…
Reference in New Issue
Block a user