From 1f444899ca225846ac54ed9e33e112a30cc8bc0f Mon Sep 17 00:00:00 2001 From: Swaminathan Vasudevan Date: Thu, 31 Mar 2016 17:48:09 -0700 Subject: [PATCH] DVR: Moving router from dvr_snat node removes the qrouters Removing the router from dvr_snat node removes the qrouters that are servicing the VM and dhcp ports. If there are still dvr serviceable ports in the dvr_snat node, and if router remove command is executed to remove the router association from the dvr_snat agent then only the snat functionality should be moved to the the different agent and the router namespaces should be untouched. This patch checks if there are any dvr serviceable ports for the dvr router and if exists, it will not send a router_remove message to the agent, but instead will send an router_update message to the agent. Change-Id: I5a3ba329346ab0d5ea7b0296ec64cc8e5fb4056d Closes-Bug: #1564575 (cherry picked from commit c198710dc551bc0f79851a7801038b033088a8c2) --- neutron/db/l3_agentschedulers_db.py | 20 +++++-- .../l3_router/test_l3_dvr_router_plugin.py | 58 +++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) 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