Add L3 Notifications To Enable BGP Dynamic Routing

This patch makes the L3 service generate notifications that enable
BGP dynamic routing to react appropriately to user-initiated changes
that affect prefixes that should be advertised and the corresponding
next-hops.

Partially-Implements: blueprint bgp-dynamic-routing
Co-Authored-By: vikram.choudhary <vikram.choudhary@huawei.com>
Change-Id: I96f14a31876efeca5881d97771e3263525522e36
This commit is contained in:
Ryan Tidwell 2016-01-03 20:37:56 +05:30 committed by Carl Baldwin
parent fe8b3299d7
commit 53138b80f3
3 changed files with 126 additions and 6 deletions

View File

@ -11,6 +11,7 @@
# under the License.
# String literals representing core resources.
FLOATING_IP = 'floating_ip'
PORT = 'port'
PROCESS = 'process'
ROUTER = 'router'

View File

@ -343,6 +343,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
if not port_requires_deletion:
return
admin_ctx = context.elevated()
old_network_id = router.gw_port['network_id']
if self.get_floatingips_count(
admin_ctx, {'router_id': [router_id]}):
@ -356,6 +357,10 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
self._check_router_gw_port_in_use(context, router_id)
self._core_plugin.delete_port(
admin_ctx, gw_port['id'], l3_port_check=False)
registry.notify(resources.ROUTER_GATEWAY,
events.AFTER_DELETE, self,
router_id=router_id,
network_id=old_network_id)
def _check_router_gw_port_in_use(self, context, router_id):
try:
@ -383,6 +388,12 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
subnet['cidr'])
self._create_router_gw_port(context, router,
new_network_id, ext_ips)
registry.notify(resources.ROUTER_GATEWAY,
events.AFTER_CREATE,
self._create_gw_port,
gw_ips=ext_ips,
network_id=new_network_id,
router_id=router_id)
def _update_current_gw_port(self, context, router_id, router, ext_ips):
self._core_plugin.update_port(context, router.gw_port['id'], {'port':
@ -667,6 +678,22 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
)
context.session.add(router_port)
gw_ips = []
gw_network_id = None
if router.gw_port:
gw_network_id = router.gw_port.network_id
gw_ips = router.gw_port.fixed_ips
registry.notify(resources.ROUTER_INTERFACE,
events.AFTER_CREATE,
self,
network_id=gw_network_id,
gateway_ips=gw_ips,
cidrs=[x['cidr'] for x in subnets],
port_id=port['id'],
router_id=router_id,
interface_info=interface_info)
return self._make_router_interface_info(
router.id, port['tenant_id'], port['id'], subnets[-1]['id'],
[subnet['id'] for subnet in subnets])
@ -771,6 +798,16 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
port, subnets = self._remove_interface_by_subnet(
context, router_id, subnet_id, device_owner)
gw_network_id = None
router = self._get_router(context, router_id)
if router.gw_port:
gw_network_id = router.gw_port.network_id
registry.notify(resources.ROUTER_INTERFACE,
events.AFTER_DELETE,
self,
cidrs=[x['cidr'] for x in subnets],
network_id=gw_network_id)
return self._make_router_interface_info(router_id, port['tenant_id'],
port['id'], subnets[0]['id'],
[subnet['id'] for subnet in
@ -948,6 +985,27 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
'fixed_port_id': port_id,
'router_id': router_id,
'last_known_router_id': previous_router_id})
next_hop = None
if router_id:
router = self._get_router(context, router_id)
gw_port = router.gw_port
for fixed_ip in gw_port.fixed_ips:
addr = netaddr.IPAddress(fixed_ip.ip_address)
if addr.version == l3_constants.IP_VERSION_4:
next_hop = fixed_ip.ip_address
break
args = {'fixed_ip_address': internal_ip_address,
'fixed_port_id': port_id,
'router_id': router_id,
'last_known_router_id': previous_router_id,
'floating_ip_address': floatingip_db.floating_ip_address,
'floating_network_id': floatingip_db.floating_network_id,
'next_hop': next_hop,
'context': context}
registry.notify(resources.FLOATING_IP,
events.AFTER_UPDATE,
self._update_fip_assoc,
**args)
def _is_ipv4_network(self, context, net_id):
net = self._core_plugin._get_network(context, net_id)

View File

@ -1619,15 +1619,16 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
exceptions.NotificationError(
'foo_callback_id', n_exc.InUse()),
]
self._router_interface_action('add',
r['router']['id'],
s['subnet']['id'],
None)
# we fail the first time, but not the second, when
# the clean-up takes place
notify.side_effect = [
exceptions.CallbackFailure(errors=errors), None
]
self._router_interface_action('add',
r['router']['id'],
s['subnet']['id'],
None)
self._router_interface_action(
'remove',
r['router']['id'],
@ -1643,11 +1644,12 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
exceptions.NotificationError(
'foo_callback_id', n_exc.InUse()),
]
notify.side_effect = exceptions.CallbackFailure(errors=errors)
self._set_net_external(s['subnet']['network_id'])
self._add_external_gateway_to_router(
r['router']['id'],
s['subnet']['network_id'])
notify.side_effect = exceptions.CallbackFailure(errors=errors)
self._remove_external_gateway_from_router(
r['router']['id'],
s['subnet']['network_id'],
@ -1912,7 +1914,7 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
self.fmt,
public_sub['subnet']['network_id'],
port_id=private_port['port']['id'],
set_context=True)
set_context=False)
self.assertTrue(agent_notification.called)
def test_floating_port_status_not_applicable(self):
@ -2022,6 +2024,65 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
self.assertEqual(str(ip_range[-2]),
body_2['floatingip']['fixed_ip_address'])
def test_first_floatingip_associate_notification(self):
with self.port() as p:
private_sub = {'subnet': {'id':
p['port']['fixed_ips'][0]['subnet_id']}}
with self.floatingip_no_assoc(private_sub) as fip:
port_id = p['port']['id']
ip_address = p['port']['fixed_ips'][0]['ip_address']
with mock.patch.object(registry, 'notify') as notify:
body = self._update('floatingips',
fip['floatingip']['id'],
{'floatingip': {'port_id': port_id}})
fip_addr = fip['floatingip']['floating_ip_address']
fip_network_id = fip['floatingip']['floating_network_id']
router_id = body['floatingip']['router_id']
body = self._show('routers', router_id)
ext_gw_info = body['router']['external_gateway_info']
ext_fixed_ip = ext_gw_info['external_fixed_ips'][0]
notify.assert_called_once_with(
resources.FLOATING_IP,
events.AFTER_UPDATE,
mock.ANY,
context=mock.ANY,
fixed_ip_address=ip_address,
fixed_port_id=port_id,
floating_ip_address=fip_addr,
floating_network_id=fip_network_id,
last_known_router_id=None,
router_id=router_id,
next_hop=ext_fixed_ip['ip_address'])
def test_floatingip_disassociate_notification(self):
with self.port() as p:
private_sub = {'subnet': {'id':
p['port']['fixed_ips'][0]['subnet_id']}}
with self.floatingip_no_assoc(private_sub) as fip:
port_id = p['port']['id']
body = self._update('floatingips',
fip['floatingip']['id'],
{'floatingip': {'port_id': port_id}})
with mock.patch.object(registry, 'notify') as notify:
fip_addr = fip['floatingip']['floating_ip_address']
fip_network_id = fip['floatingip']['floating_network_id']
router_id = body['floatingip']['router_id']
self._update('floatingips',
fip['floatingip']['id'],
{'floatingip': {'port_id': None}})
notify.assert_called_once_with(
resources.FLOATING_IP,
events.AFTER_UPDATE,
mock.ANY,
context=mock.ANY,
fixed_ip_address=None,
fixed_port_id=None,
floating_ip_address=fip_addr,
floating_network_id=fip_network_id,
last_known_router_id=router_id,
router_id=None,
next_hop=None)
def test_floatingip_update_different_router(self):
# Create subnet with different CIDRs to account for plugins which
# do not support overlapping IPs