diff --git a/neutron/db/l3_agentschedulers_db.py b/neutron/db/l3_agentschedulers_db.py index 0298251a081..54aa10cba2e 100644 --- a/neutron/db/l3_agentschedulers_db.py +++ b/neutron/db/l3_agentschedulers_db.py @@ -255,13 +255,25 @@ class L3AgentSchedulerDbMixin(l3agentscheduler.L3AgentSchedulerPluginBase, self._unbind_router(context, router_id, agent_id) router = self.get_router(context, router_id) + plugin = manager.NeutronManager.get_service_plugins().get( + service_constants.L3_ROUTER_NAT) if router.get('ha'): - plugin = manager.NeutronManager.get_service_plugins().get( - service_constants.L3_ROUTER_NAT) plugin.delete_ha_interfaces_on_host(context, router_id, agent.host) - + # NOTE(Swami): Need to verify if there are DVR serviceable + # ports owned by this agent. If owned by this agent, then + # the routers should be retained. This flag will be used + # to check if there are valid routers in this agent. + retain_router = False + if router.get('distributed'): + subnet_ids = plugin.get_subnet_ids_on_router(context, router_id) + if subnet_ids and agent.host: + retain_router = plugin._check_dvr_serviceable_ports_on_host( + context, agent.host, subnet_ids) l3_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_L3) - if l3_notifier: + if retain_router and l3_notifier: + l3_notifier.routers_updated_on_host( + context, [router_id], agent.host) + elif l3_notifier: l3_notifier.router_removed_from_agent( context, router_id, agent.host) 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 b64cb3a3a65..56c8a6ba386 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 @@ -831,6 +831,64 @@ class L3DvrTestCase(ml2_test_base.ML2TestFramework): l3_notifier.router_removed_from_agent.assert_called_once_with( mock.ANY, router['id'], HOST1) + def test_dvr_router_manual_rescheduling_removes_router(self): + router = self._create_router() + kwargs = {'arg_list': (external_net.EXTERNAL,), + external_net.EXTERNAL: True} + with self.network(**kwargs) as ext_net,\ + self.subnet(network=ext_net),\ + self.subnet(cidr='20.0.0.0/24') as subnet,\ + self.port(subnet=subnet): + self.l3_plugin._update_router_gw_info( + self.context, router['id'], + {'network_id': ext_net['network']['id']}) + self.l3_plugin.add_router_interface( + self.context, router['id'], + {'subnet_id': subnet['subnet']['id']}) + self.l3_plugin.schedule_router(self.context, + router['id'], + candidates=[self.l3_agent]) + # Now the VM should be also scheduled on the node + notifier = self.l3_plugin.agent_notifiers[ + constants.AGENT_TYPE_L3] + with mock.patch.object( + notifier, 'router_removed_from_agent') as rtr_remove_mock: + self.l3_plugin.remove_router_from_l3_agent( + self.context, self.l3_agent['id'], router['id']) + rtr_remove_mock.assert_called_once_with( + self.context, router['id'], self.l3_agent['host']) + + def test_dvr_router_manual_rescheduling_updates_router(self): + router = self._create_router() + kwargs = {'arg_list': (external_net.EXTERNAL,), + external_net.EXTERNAL: True} + with self.network(**kwargs) as ext_net,\ + self.subnet(network=ext_net),\ + self.subnet(cidr='20.0.0.0/24') as subnet,\ + self.port(subnet=subnet, + device_owner=DEVICE_OWNER_COMPUTE) as port: + self.core_plugin.update_port( + self.context, port['port']['id'], + {'port': {'binding:host_id': self.l3_agent['host']}}) + self.l3_plugin._update_router_gw_info( + self.context, router['id'], + {'network_id': ext_net['network']['id']}) + self.l3_plugin.add_router_interface( + self.context, router['id'], + {'subnet_id': subnet['subnet']['id']}) + self.l3_plugin.schedule_router(self.context, + router['id'], + candidates=[self.l3_agent]) + # Now the VM should be also scheduled on the node + notifier = self.l3_plugin.agent_notifiers[ + constants.AGENT_TYPE_L3] + with mock.patch.object( + notifier, 'routers_updated_on_host') as rtr_update_mock: + self.l3_plugin.remove_router_from_l3_agent( + self.context, self.l3_agent['id'], router['id']) + rtr_update_mock.assert_called_once_with( + self.context, [router['id']], self.l3_agent['host']) + def _test_router_remove_from_agent_on_vm_port_deletion( self, non_admin_port=False): # register l3 agent in dvr mode in addition to existing dvr_snat agent