diff --git a/neutron/callbacks/resources.py b/neutron/callbacks/resources.py index a0fd4c09eae..d6d8efa1ab8 100644 --- a/neutron/callbacks/resources.py +++ b/neutron/callbacks/resources.py @@ -11,6 +11,7 @@ # under the License. # String literals representing core resources. +FLOATING_IP = 'floating_ip' PORT = 'port' PROCESS = 'process' ROUTER = 'router' diff --git a/neutron/db/l3_db.py b/neutron/db/l3_db.py index 23ea98c3746..e4d11fd8a86 100644 --- a/neutron/db/l3_db.py +++ b/neutron/db/l3_db.py @@ -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) diff --git a/neutron/tests/unit/extensions/test_l3.py b/neutron/tests/unit/extensions/test_l3.py index 9bdaaccd1fd..14ee0db82b1 100644 --- a/neutron/tests/unit/extensions/test_l3.py +++ b/neutron/tests/unit/extensions/test_l3.py @@ -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