From e6ddea1aa0c17fd16e746fe49696a675894f7209 Mon Sep 17 00:00:00 2001 From: liuyulong Date: Mon, 1 Mar 2021 02:39:08 +0000 Subject: [PATCH] Revert "DVR: Remove control plane arp updates for DVR" This reverts commit b3a42cddc5c8716091d2b403831fb406d9dfcc75. Reason for revert: bug/1916761 Conflicts: neutron/db/l3_dvr_db.py Change-Id: I12b5e672b01f64fa118fe50d3add09ac2ae419c5 --- neutron/db/l3_dvr_db.py | 100 +++++++ neutron/db/l3_dvrscheduler_db.py | 47 ++++ .../l3_router/test_l3_dvr_router_plugin.py | 243 ++++++++++++++++++ neutron/tests/unit/db/test_l3_dvr_db.py | 66 +++++ .../unit/scheduler/test_l3_agent_scheduler.py | 44 ++++ 5 files changed, 500 insertions(+) diff --git a/neutron/db/l3_dvr_db.py b/neutron/db/l3_dvr_db.py index c5db0de4731..6a08b8ae248 100644 --- a/neutron/db/l3_dvr_db.py +++ b/neutron/db/l3_dvr_db.py @@ -13,6 +13,7 @@ # under the License. import collections +import netaddr from neutron_lib.api.definitions import external_net as extnet_apidef from neutron_lib.api.definitions import l3 as l3_apidef from neutron_lib.api.definitions import portbindings @@ -49,6 +50,7 @@ from neutron.db.models import allowed_address_pair as aap_models from neutron.db import models_v2 from neutron.ipam import utils as ipam_utils from neutron.objects import agent as ag_obj +from neutron.objects import base as base_obj from neutron.objects import l3agent as rb_obj from neutron.objects import router as l3_obj @@ -479,6 +481,17 @@ class DVRResourceOperationHandler(object): fixed_ip_address)) if not addr_pair_active_service_port_list: return + self._inherit_service_port_and_arp_update( + context, addr_pair_active_service_port_list[0]) + + def _inherit_service_port_and_arp_update(self, context, service_port): + """Function inherits port host bindings for allowed_address_pair.""" + service_port_dict = self.l3plugin._core_plugin._make_port_dict( + service_port) + address_pair_list = service_port_dict.get('allowed_address_pairs') + for address_pair in address_pair_list: + self.update_arp_entry_for_dvr_service_port(context, + service_port_dict) @registry.receives(resources.ROUTER_INTERFACE, [events.BEFORE_CREATE]) @db_api.retry_if_session_inactive() @@ -1114,6 +1127,21 @@ class _DVRAgentInterfaceMixin(object): self._populate_mtu_and_subnets_for_ports(context, [agent_port]) return agent_port + def _generate_arp_table_and_notify_agent(self, context, fixed_ip, + mac_address, notifier): + """Generates the arp table entry and notifies the l3 agent.""" + ip_address = fixed_ip['ip_address'] + subnet = fixed_ip['subnet_id'] + arp_table = {'ip_address': ip_address, + 'mac_address': mac_address, + 'subnet_id': subnet} + filters = {'fixed_ips': {'subnet_id': [subnet]}, + 'device_owner': [const.DEVICE_OWNER_DVR_INTERFACE]} + ports = self._core_plugin.get_ports(context, filters=filters) + routers = [port['device_id'] for port in ports] + for router_id in routers: + notifier(context, router_id, arp_table) + def _get_subnet_id_for_given_fixed_ip(self, context, fixed_ip, port_dict): """Returns the subnet_id that matches the fixedip on a network.""" filters = {'network_id': [port_dict['network_id']]} @@ -1122,6 +1150,78 @@ class _DVRAgentInterfaceMixin(object): if ipam_utils.check_subnet_ip(subnet['cidr'], fixed_ip): return subnet['id'] + def _get_allowed_address_pair_fixed_ips(self, context, port_dict): + """Returns all fixed_ips associated with the allowed_address_pair.""" + aa_pair_fixed_ips = [] + if port_dict.get('allowed_address_pairs'): + for address_pair in port_dict['allowed_address_pairs']: + aap_ip_cidr = address_pair['ip_address'].split("/") + if len(aap_ip_cidr) == 1 or int(aap_ip_cidr[1]) == 32: + subnet_id = self._get_subnet_id_for_given_fixed_ip( + context, aap_ip_cidr[0], port_dict) + if subnet_id is not None: + fixed_ip = {'subnet_id': subnet_id, + 'ip_address': aap_ip_cidr[0]} + aa_pair_fixed_ips.append(fixed_ip) + else: + LOG.debug("Subnet does not match for the given " + "fixed_ip %s for arp update", aap_ip_cidr[0]) + return aa_pair_fixed_ips + + def update_arp_entry_for_dvr_service_port(self, context, port_dict): + """Notify L3 agents of ARP table entry for dvr service port. + + When a dvr service port goes up, look for the DVR router on + the port's subnet, and send the ARP details to all + L3 agents hosting the router to add it. + If there are any allowed_address_pairs associated with the port + those fixed_ips should also be updated in the ARP table. + """ + fixed_ips = port_dict['fixed_ips'] + if not fixed_ips: + return + allowed_address_pair_fixed_ips = ( + self._get_allowed_address_pair_fixed_ips(context, port_dict)) + changed_fixed_ips = fixed_ips + allowed_address_pair_fixed_ips + for fixed_ip in changed_fixed_ips: + self._generate_arp_table_and_notify_agent( + context, fixed_ip, port_dict['mac_address'], + self.l3_rpc_notifier.add_arp_entry) + + def delete_arp_entry_for_dvr_service_port(self, context, port_dict, + fixed_ips_to_delete=None): + """Notify L3 agents of ARP table entry for dvr service port. + + When a dvr service port goes down, look for the DVR + router on the port's subnet, and send the ARP details to all + L3 agents hosting the router to delete it. + If there are any allowed_address_pairs associated with the + port, those fixed_ips should be removed from the ARP table. + """ + fixed_ips = port_dict['fixed_ips'] + if not fixed_ips: + return + if not fixed_ips_to_delete: + allowed_address_pair_fixed_ips = ( + self._get_allowed_address_pair_fixed_ips(context, port_dict)) + fixed_ips_to_delete = fixed_ips + allowed_address_pair_fixed_ips + for fixed_ip in fixed_ips_to_delete: + self._generate_arp_table_and_notify_agent( + context, fixed_ip, port_dict['mac_address'], + self.l3_rpc_notifier.del_arp_entry) + + def _get_address_pair_active_port_with_fip( + self, context, port_dict, port_addr_pair_ip): + port_valid_state = (port_dict['admin_state_up'] or + port_dict['status'] == const.PORT_STATUS_ACTIVE) + if not port_valid_state: + return + fips = l3_obj.FloatingIP.get_objects( + context, _pager=base_obj.Pager(limit=1), + fixed_ip_address=netaddr.IPAddress(port_addr_pair_ip)) + return self._core_plugin.get_port( + context, fips[0].fixed_port_id) if fips else None + class L3_NAT_with_dvr_db_mixin(_DVRAgentInterfaceMixin, DVRResourceOperationHandler, diff --git a/neutron/db/l3_dvrscheduler_db.py b/neutron/db/l3_dvrscheduler_db.py index b79d57b8cd0..53527bf4d37 100644 --- a/neutron/db/l3_dvrscheduler_db.py +++ b/neutron/db/l3_dvrscheduler_db.py @@ -518,6 +518,19 @@ class L3_DVRsch_db_mixin(l3agent_sch_db.L3AgentSchedulerDbMixin): return any([r in dvr_routers for r in related_routers]) +def _dvr_handle_unbound_allowed_addr_pair_add( + plugin, context, port, allowed_address_pair): + plugin.update_arp_entry_for_dvr_service_port(context, port) + + +def _dvr_handle_unbound_allowed_addr_pair_del( + plugin, context, port, allowed_address_pair): + aa_fixed_ips = plugin._get_allowed_address_pair_fixed_ips(context, port) + if aa_fixed_ips: + plugin.delete_arp_entry_for_dvr_service_port( + context, port, fixed_ips_to_delete=aa_fixed_ips) + + def _notify_l3_agent_new_port(resource, event, trigger, **kwargs): LOG.debug('Received %(resource)s %(event)s', { 'resource': resource, @@ -530,6 +543,7 @@ def _notify_l3_agent_new_port(resource, event, trigger, **kwargs): l3plugin = directory.get_plugin(plugin_constants.L3) context = kwargs['context'] l3plugin.dvr_handle_new_service_port(context, port) + l3plugin.update_arp_entry_for_dvr_service_port(context, port) def _notify_port_delete(event, resource, trigger, **kwargs): @@ -537,6 +551,14 @@ def _notify_port_delete(event, resource, trigger, **kwargs): port = kwargs['port'] get_related_hosts_info = kwargs.get("get_related_hosts_info", True) l3plugin = directory.get_plugin(plugin_constants.L3) + if port: + port_host = port.get(portbindings.HOST_ID) + allowed_address_pairs_list = port.get('allowed_address_pairs') + if allowed_address_pairs_list and port_host: + for address_pair in allowed_address_pairs_list: + _dvr_handle_unbound_allowed_addr_pair_del( + l3plugin, context, port, address_pair) + l3plugin.delete_arp_entry_for_dvr_service_port(context, port) removed_routers = l3plugin.get_dvr_routers_to_remove( context, port, get_related_hosts_info) for info in removed_routers: @@ -625,7 +647,32 @@ def _notify_l3_agent_port_update(resource, event, trigger, **kwargs): context, new_port, dest_host=dest_host, router_id=fip_router_id) + l3plugin.update_arp_entry_for_dvr_service_port( + context, new_port) return + # Check for allowed_address_pairs and port state + new_port_host = new_port.get(portbindings.HOST_ID) + allowed_address_pairs_list = new_port.get('allowed_address_pairs') + if allowed_address_pairs_list and new_port_host: + new_port_state = new_port.get('admin_state_up') + original_port_state = original_port.get('admin_state_up') + if new_port_state: + # Case were we activate the port from inactive state, + # or the same port has additional address_pairs added. + for address_pair in allowed_address_pairs_list: + _dvr_handle_unbound_allowed_addr_pair_add( + l3plugin, context, new_port, address_pair) + return + elif original_port_state: + # Case were we deactivate the port from active state. + for address_pair in allowed_address_pairs_list: + _dvr_handle_unbound_allowed_addr_pair_del( + l3plugin, context, original_port, address_pair) + return + + if kwargs.get('mac_address_updated') or is_fixed_ips_changed: + l3plugin.update_arp_entry_for_dvr_service_port( + context, new_port) def subscribe(): diff --git a/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py b/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py index 5bce946fc76..74639edaa00 100644 --- a/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py +++ b/neutron/tests/functional/services/l3_router/test_l3_dvr_router_plugin.py @@ -1025,6 +1025,145 @@ class L3DvrTestCase(L3DvrTestCaseBase): floatingips = router_sync_info[0][constants.FLOATINGIP_KEY] self.assertTrue(floatingips[0][constants.DVR_SNAT_BOUND]) + def test_allowed_addr_pairs_delayed_fip_and_update_arp_entry(self): + HOST1 = 'host1' + helpers.register_l3_agent( + host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR) + HOST2 = 'host2' + helpers.register_l3_agent( + host=HOST2, agent_mode=constants.L3_AGENT_MODE_DVR) + router = self._create_router(ha=False) + private_net1 = self._make_network(self.fmt, 'net1', True) + test_allocation_pools = [{'start': '10.1.0.2', + 'end': '10.1.0.20'}] + fixed_vrrp_ip = [{'ip_address': '10.1.0.201'}] + kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), + extnet_apidef.EXTERNAL: True} + ext_net = self._make_network(self.fmt, '', True, **kwargs) + self._make_subnet( + self.fmt, ext_net, '10.20.0.1', '10.20.0.0/24', + ip_version=constants.IP_VERSION_4, enable_dhcp=True) + self.l3_plugin.schedule_router(self.context, + router['id'], + candidates=[self.l3_agent]) + + # Set gateway to router + self.l3_plugin._update_router_gw_info( + self.context, router['id'], + {'network_id': ext_net['network']['id']}) + private_subnet1 = self._make_subnet( + self.fmt, + private_net1, + '10.1.0.1', + cidr='10.1.0.0/24', + ip_version=constants.IP_VERSION_4, + allocation_pools=test_allocation_pools, + enable_dhcp=True) + vrrp_port = self._make_port( + self.fmt, + private_net1['network']['id'], + fixed_ips=fixed_vrrp_ip) + allowed_address_pairs = [ + {'ip_address': '10.1.0.201', + 'mac_address': vrrp_port['port']['mac_address']}] + with self.port( + subnet=private_subnet1, + device_owner=DEVICE_OWNER_COMPUTE) as int_port,\ + self.port(subnet=private_subnet1, + device_owner=DEVICE_OWNER_COMPUTE) as int_port2: + self.l3_plugin.add_router_interface( + self.context, router['id'], + {'subnet_id': private_subnet1['subnet']['id']}) + router_handle = ( + self.l3_plugin.list_active_sync_routers_on_active_l3_agent( + self.context, self.l3_agent['host'], [router['id']])) + self.assertEqual(self.l3_agent['host'], + router_handle[0]['gw_port_host']) + with mock.patch.object(self.l3_plugin, + '_l3_rpc_notifier') as l3_notifier: + vm_port = self.core_plugin.update_port( + self.context, int_port['port']['id'], + {'port': {portbindings.HOST_ID: HOST1}}) + vm_port_mac = vm_port['mac_address'] + vm_port_fixed_ips = vm_port['fixed_ips'] + vm_port_subnet_id = vm_port_fixed_ips[0]['subnet_id'] + vm_arp_table = { + 'ip_address': vm_port_fixed_ips[0]['ip_address'], + 'mac_address': vm_port_mac, + 'subnet_id': vm_port_subnet_id} + vm_port2 = self.core_plugin.update_port( + self.context, int_port2['port']['id'], + {'port': {portbindings.HOST_ID: HOST2}}) + # Now update the VM port with the allowed_address_pair + self.core_plugin.update_port( + self.context, vm_port['id'], + {'port': { + 'allowed_address_pairs': allowed_address_pairs}}) + self.core_plugin.update_port( + self.context, vm_port2['id'], + {'port': { + 'allowed_address_pairs': allowed_address_pairs}}) + self.assertEqual( + 2, l3_notifier.routers_updated_on_host.call_count) + updated_vm_port1 = self.core_plugin.get_port( + self.context, vm_port['id']) + updated_vm_port2 = self.core_plugin.get_port( + self.context, vm_port2['id']) + expected_allowed_address_pairs = updated_vm_port1.get( + 'allowed_address_pairs') + self.assertEqual(expected_allowed_address_pairs, + allowed_address_pairs) + expected_allowed_address_pairs_2 = updated_vm_port2.get( + 'allowed_address_pairs') + self.assertEqual(expected_allowed_address_pairs_2, + allowed_address_pairs) + # Now the VRRP port is attached to the VM port. At this + # point, the VRRP port should not have inherited the + # port host bindings from the parent VM port. + cur_vrrp_port_db = self.core_plugin.get_port( + self.context, vrrp_port['port']['id']) + self.assertNotEqual( + cur_vrrp_port_db[portbindings.HOST_ID], HOST1) + self.assertNotEqual( + cur_vrrp_port_db[portbindings.HOST_ID], HOST2) + # Next we can try to associate the floatingip to the + # VRRP port that is already attached to the VM port + floating_ip = {'floating_network_id': ext_net['network']['id'], + 'router_id': router['id'], + 'port_id': vrrp_port['port']['id'], + 'tenant_id': vrrp_port['port']['tenant_id']} + floating_ip = self.l3_plugin.create_floatingip( + self.context, {'floatingip': floating_ip}) + + post_update_vrrp_port_db = self.core_plugin.get_port( + self.context, vrrp_port['port']['id']) + vrrp_port_fixed_ips = post_update_vrrp_port_db['fixed_ips'] + vrrp_port_subnet_id = vrrp_port_fixed_ips[0]['subnet_id'] + vrrp_arp_table1 = { + 'ip_address': vrrp_port_fixed_ips[0]['ip_address'], + 'mac_address': vm_port_mac, + 'subnet_id': vrrp_port_subnet_id} + + expected_calls = [ + mock.call(self.context, + router['id'], vm_arp_table), + mock.call(self.context, + router['id'], vrrp_arp_table1)] + l3_notifier.add_arp_entry.assert_has_calls( + expected_calls) + expected_routers_updated_calls = [ + mock.call(self.context, mock.ANY, HOST1), + mock.call(self.context, mock.ANY, HOST2), + mock.call(self.context, mock.ANY, 'host0')] + l3_notifier.routers_updated_on_host.assert_has_calls( + expected_routers_updated_calls, any_order=True) + self.assertFalse(l3_notifier.routers_updated.called) + router_info = ( + self.l3_plugin.list_active_sync_routers_on_active_l3_agent( + self.context, self.l3_agent['host'], [router['id']])) + floatingips = router_info[0][constants.FLOATINGIP_KEY] + self.assertTrue(floatingips[0][constants.DVR_SNAT_BOUND]) + def test_dvr_gateway_host_binding_is_set(self): router = self._create_router(ha=False) private_net1 = self._make_network(self.fmt, 'net1', True) @@ -1059,6 +1198,110 @@ class L3DvrTestCase(L3DvrTestCaseBase): self.assertEqual(self.l3_agent['host'], router_handle[0]['gw_port_host']) + def test_allowed_address_pairs_update_arp_entry(self): + HOST1 = 'host1' + helpers.register_l3_agent( + host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR) + router = self._create_router(ha=False) + private_net1 = self._make_network(self.fmt, 'net1', True) + test_allocation_pools = [{'start': '10.1.0.2', + 'end': '10.1.0.20'}] + fixed_vrrp_ip = [{'ip_address': '10.1.0.201'}] + kwargs = {'arg_list': (extnet_apidef.EXTERNAL,), + extnet_apidef.EXTERNAL: True} + ext_net = self._make_network(self.fmt, '', True, **kwargs) + self._make_subnet( + self.fmt, ext_net, '10.20.0.1', '10.20.0.0/24', + ip_version=constants.IP_VERSION_4, enable_dhcp=True) + self.l3_plugin.schedule_router(self.context, + router['id'], + candidates=[self.l3_agent]) + # Set gateway to router + self.l3_plugin._update_router_gw_info( + self.context, router['id'], + {'network_id': ext_net['network']['id']}) + private_subnet1 = self._make_subnet( + self.fmt, + private_net1, + '10.1.0.1', + cidr='10.1.0.0/24', + ip_version=constants.IP_VERSION_4, + allocation_pools=test_allocation_pools, + enable_dhcp=True) + vrrp_port = self._make_port( + self.fmt, + private_net1['network']['id'], + fixed_ips=fixed_vrrp_ip) + allowed_address_pairs = [ + {'ip_address': '10.1.0.201', + 'mac_address': vrrp_port['port']['mac_address']}] + with self.port( + subnet=private_subnet1, + device_owner=DEVICE_OWNER_COMPUTE) as int_port: + self.l3_plugin.add_router_interface( + self.context, router['id'], + {'subnet_id': private_subnet1['subnet']['id']}) + router_handle = ( + self.l3_plugin.list_active_sync_routers_on_active_l3_agent( + self.context, self.l3_agent['host'], [router['id']])) + self.assertEqual(self.l3_agent['host'], + router_handle[0]['gw_port_host']) + with mock.patch.object(self.l3_plugin, + '_l3_rpc_notifier') as l3_notifier: + vm_port = self.core_plugin.update_port( + self.context, int_port['port']['id'], + {'port': {portbindings.HOST_ID: HOST1}}) + vm_port_mac = vm_port['mac_address'] + vm_port_fixed_ips = vm_port['fixed_ips'] + vm_port_subnet_id = vm_port_fixed_ips[0]['subnet_id'] + vm_arp_table = { + 'ip_address': vm_port_fixed_ips[0]['ip_address'], + 'mac_address': vm_port_mac, + 'subnet_id': vm_port_subnet_id} + self.assertEqual(1, l3_notifier.add_arp_entry.call_count) + floating_ip = {'floating_network_id': ext_net['network']['id'], + 'router_id': router['id'], + 'port_id': vrrp_port['port']['id'], + 'tenant_id': vrrp_port['port']['tenant_id']} + floating_ip = self.l3_plugin.create_floatingip( + self.context, {'floatingip': floating_ip}) + vrrp_port_db = self.core_plugin.get_port( + self.context, vrrp_port['port']['id']) + self.assertNotEqual(vrrp_port_db[portbindings.HOST_ID], HOST1) + # Now update the VM port with the allowed_address_pair + self.core_plugin.update_port( + self.context, vm_port['id'], + {'port': { + 'allowed_address_pairs': allowed_address_pairs}}) + updated_vm_port = self.core_plugin.get_port( + self.context, vm_port['id']) + expected_allowed_address_pairs = updated_vm_port.get( + 'allowed_address_pairs') + self.assertEqual(expected_allowed_address_pairs, + allowed_address_pairs) + cur_vrrp_port_db = self.core_plugin.get_port( + self.context, vrrp_port['port']['id']) + vrrp_port_fixed_ips = cur_vrrp_port_db['fixed_ips'] + vrrp_port_subnet_id = vrrp_port_fixed_ips[0]['subnet_id'] + vrrp_arp_table1 = { + 'ip_address': vrrp_port_fixed_ips[0]['ip_address'], + 'mac_address': vm_port_mac, + 'subnet_id': vrrp_port_subnet_id} + + expected_calls = [ + mock.call(self.context, + router['id'], vm_arp_table), + mock.call(self.context, + router['id'], vrrp_arp_table1)] + l3_notifier.add_arp_entry.assert_has_calls( + expected_calls) + expected_routers_updated_calls = [ + mock.call(self.context, mock.ANY, HOST1), + mock.call(self.context, mock.ANY, 'host0')] + l3_notifier.routers_updated_on_host.assert_has_calls( + expected_routers_updated_calls) + self.assertFalse(l3_notifier.routers_updated.called) + def test_update_vm_port_host_router_update(self): # register l3 agents in dvr mode in addition to existing dvr_snat agent HOST1 = 'host1' diff --git a/neutron/tests/unit/db/test_l3_dvr_db.py b/neutron/tests/unit/db/test_l3_dvr_db.py index c886f558bba..21699e49c79 100644 --- a/neutron/tests/unit/db/test_l3_dvr_db.py +++ b/neutron/tests/unit/db/test_l3_dvr_db.py @@ -1191,6 +1191,72 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): interface_info=interface_info) self._assert_mock_called_with_router(mock_notify, router_db.id) + def test__generate_arp_table_and_notify_agent(self): + fixed_ip = { + 'ip_address': '1.2.3.4', + 'subnet_id': _uuid()} + mac_address = "00:11:22:33:44:55" + expected_arp_table = { + 'ip_address': fixed_ip['ip_address'], + 'subnet_id': fixed_ip['subnet_id'], + 'mac_address': mac_address} + notifier = mock.Mock() + ports = [{'id': _uuid(), 'device_id': 'router_1'}, + {'id': _uuid(), 'device_id': 'router_2'}] + with mock.patch.object(self.core_plugin, "get_ports", + return_value=ports): + self.mixin._generate_arp_table_and_notify_agent( + self.ctx, fixed_ip, mac_address, notifier) + notifier.assert_has_calls([ + mock.call(self.ctx, "router_1", expected_arp_table), + mock.call(self.ctx, "router_2", expected_arp_table)]) + + def _test_update_arp_entry_for_dvr_service_port( + self, device_owner, action): + router_dict = {'name': 'test_router', 'admin_state_up': True, + 'distributed': True} + router = self._create_router(router_dict) + plugin = mock.Mock() + directory.add_plugin(plugin_constants.CORE, plugin) + l3_notify = self.mixin.l3_rpc_notifier = mock.Mock() + port = { + 'id': 'my_port_id', + 'fixed_ips': [ + {'subnet_id': '51edc9e0-24f9-47f2-8e1e-2a41cb691323', + 'ip_address': '10.0.0.11'}, + {'subnet_id': '2b7c8a07-6f8e-4937-8701-f1d5da1a807c', + 'ip_address': '10.0.0.21'}, + {'subnet_id': '48534187-f077-4e81-93ff-81ec4cc0ad3b', + 'ip_address': 'fd45:1515:7e0:0:f816:3eff:fe1a:1111'}], + 'mac_address': 'my_mac', + 'device_owner': device_owner + } + dvr_port = { + 'id': 'dvr_port_id', + 'fixed_ips': mock.ANY, + 'device_owner': const.DEVICE_OWNER_DVR_INTERFACE, + 'device_id': router['id'] + } + plugin.get_ports.return_value = [dvr_port] + if action == 'add': + self.mixin.update_arp_entry_for_dvr_service_port( + self.ctx, port) + self.assertEqual(3, l3_notify.add_arp_entry.call_count) + elif action == 'del': + self.mixin.delete_arp_entry_for_dvr_service_port( + self.ctx, port) + self.assertEqual(3, l3_notify.del_arp_entry.call_count) + + def test_update_arp_entry_for_dvr_service_port_added(self): + action = 'add' + device_owner = const.DEVICE_OWNER_LOADBALANCER + self._test_update_arp_entry_for_dvr_service_port(device_owner, action) + + def test_update_arp_entry_for_dvr_service_port_deleted(self): + action = 'del' + device_owner = const.DEVICE_OWNER_LOADBALANCER + self._test_update_arp_entry_for_dvr_service_port(device_owner, action) + def test_add_router_interface_csnat_ports_failure(self): router_dict = {'name': 'test_router', 'admin_state_up': True, 'distributed': True} diff --git a/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py b/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py index 9cb33542b46..22db064d7f6 100644 --- a/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py +++ b/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py @@ -841,12 +841,20 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, 'device_owner': DEVICE_OWNER_COMPUTE, }, } + port = kwargs.get('original_port') l3plugin = mock.Mock() directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) l3plugin._get_allowed_address_pair_fixed_ips.return_value = ( ['10.1.0.21']) + self.assertFalse( + l3plugin.update_arp_entry_for_dvr_service_port.called) + l3plugin.delete_arp_entry_for_dvr_service_port.\ + assert_called_once_with( + self.adminContext, + port, + fixed_ips_to_delete=mock.ANY) def test__notify_l3_agent_update_port_with_allowed_address_pairs(self): port_id = uuidutils.generate_uuid() @@ -874,6 +882,8 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) + self.assertTrue( + l3plugin.update_arp_entry_for_dvr_service_port.called) def test__notify_l3_agent_when_unbound_port_migrates_to_bound_host(self): port_id = 'fake-port' @@ -929,6 +939,8 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', plugin, **kwargs) + self.assertFalse( + l3plugin.update_arp_entry_for_dvr_service_port.called) self.assertFalse( l3plugin.dvr_handle_new_service_port.called) self.assertFalse(l3plugin.remove_router_from_l3_agent.called) @@ -946,6 +958,9 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_new_port( 'port', 'after_create', mock.ANY, **kwargs) + l3plugin.update_arp_entry_for_dvr_service_port.\ + assert_called_once_with( + self.adminContext, kwargs.get('port')) l3plugin.dvr_handle_new_service_port.assert_called_once_with( self.adminContext, kwargs.get('port')) @@ -961,6 +976,8 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_new_port( 'port', 'after_create', mock.ANY, **kwargs) + self.assertFalse( + l3plugin.update_arp_entry_for_dvr_service_port.called) self.assertFalse( l3plugin.dvr_handle_new_service_port.called) @@ -987,6 +1004,9 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, l3plugin.dvr_handle_new_service_port.assert_called_once_with( self.adminContext, kwargs.get('port'), dest_host='vm-host2', router_id=None) + l3plugin.update_arp_entry_for_dvr_service_port.\ + assert_called_once_with( + self.adminContext, kwargs.get('port')) def test__notify_l3_agent_update_port_no_action(self): kwargs = { @@ -1005,6 +1025,8 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) + self.assertFalse( + l3plugin.update_arp_entry_for_dvr_service_port.called) self.assertFalse( l3plugin.dvr_handle_new_service_port.called) self.assertFalse(l3plugin.remove_router_from_l3_agent.called) @@ -1029,6 +1051,10 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, directory.add_plugin(plugin_constants.L3, l3plugin) l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) + + l3plugin.update_arp_entry_for_dvr_service_port.\ + assert_called_once_with( + self.adminContext, kwargs.get('port')) self.assertFalse(l3plugin.dvr_handle_new_service_port.called) def test__notify_l3_agent_update_port_with_ip_update(self): @@ -1053,6 +1079,9 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) + l3plugin.update_arp_entry_for_dvr_service_port.\ + assert_called_once_with( + self.adminContext, kwargs.get('port')) self.assertFalse(l3plugin.dvr_handle_new_service_port.called) def test__notify_l3_agent_update_port_without_ip_change(self): @@ -1074,6 +1103,7 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', mock.ANY, **kwargs) + self.assertFalse(l3plugin.update_arp_entry_for_dvr_service_port.called) self.assertFalse(l3plugin.dvr_handle_new_service_port.called) def test__notify_l3_agent_port_binding_change(self): @@ -1159,10 +1189,15 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, if routers_to_remove: (l3plugin.l3_rpc_notifier.router_removed_from_agent. assert_called_once_with(mock.ANY, 'foo_id', source_host)) + self.assertEqual( + 1, + l3plugin.delete_arp_entry_for_dvr_service_port.call_count) if fip and is_distributed and not (routers_to_remove and fip['router_id'] is routers_to_remove[0]['router_id']): (l3plugin.l3_rpc_notifier.routers_updated_on_host. assert_called_once_with(mock.ANY, ['router_id'], source_host)) + self.assertEqual( + 1, l3plugin.update_arp_entry_for_dvr_service_port.call_count) l3plugin.dvr_handle_new_service_port.assert_called_once_with( self.adminContext, kwargs.get('port'), dest_host=None, router_id=router_id) @@ -1203,6 +1238,12 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, l3_dvrscheduler_db._notify_l3_agent_port_update( 'port', 'after_update', plugin, **kwargs) + self.assertEqual( + 1, l3plugin.delete_arp_entry_for_dvr_service_port.call_count) + l3plugin.delete_arp_entry_for_dvr_service_port.\ + assert_called_once_with( + self.adminContext, mock.ANY) + self.assertFalse( l3plugin.dvr_handle_new_service_port.called) (l3plugin.l3_rpc_notifier.router_removed_from_agent. @@ -1236,6 +1277,9 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, l3plugin.get_dvr_routers_to_remove.return_value = removed_routers l3_dvrscheduler_db._notify_port_delete( 'port', 'after_delete', plugin, **kwargs) + l3plugin.delete_arp_entry_for_dvr_service_port.\ + assert_called_once_with( + self.adminContext, mock.ANY) (l3plugin.l3_rpc_notifier.router_removed_from_agent. assert_called_once_with(mock.ANY, 'foo_id', 'foo_host'))