From 189a6f1bc3295b719304f44e5b731ef671fe7f50 Mon Sep 17 00:00:00 2001 From: Oleg Bondarev Date: Thu, 22 Oct 2015 13:23:21 +0300 Subject: [PATCH] DVR: handle dvr serviceable port's host change When a VM port's host is changed we need to check if a router should be unscheduled from old host and send corresponding notifications. commit d5a8074ec3c67ed68e64a96827da990f1c34e10f added such a check when port is unbound. This patch adds similar check in case of host change (instance live migration) Closes-Bug: #1508869 Change-Id: I57fa8253b2c88f7b7380a79b841fc424e9e52f19 --- neutron/db/l3_dvrscheduler_db.py | 13 ++++- .../l3_router/test_l3_dvr_router_plugin.py | 57 ++++++++++++++----- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/neutron/db/l3_dvrscheduler_db.py b/neutron/db/l3_dvrscheduler_db.py index 3d0730282..c0941c78f 100644 --- a/neutron/db/l3_dvrscheduler_db.py +++ b/neutron/db/l3_dvrscheduler_db.py @@ -491,8 +491,14 @@ def _notify_l3_agent_port_update(resource, event, trigger, **kwargs): if new_port and original_port: original_device_owner = original_port.get('device_owner', '') - if (original_device_owner.startswith('compute') and - not new_port.get('device_owner')): + new_device_owner = new_port.get('device_owner', '') + is_port_no_longer_serviced = ( + n_utils.is_dvr_serviced(original_device_owner) and + not n_utils.is_dvr_serviced(new_device_owner)) + is_port_moved = ( + original_port['binding:host_id'] and + original_port['binding:host_id'] != new_port['binding:host_id']) + if is_port_no_longer_serviced or is_port_moved: l3plugin = manager.NeutronManager.get_service_plugins().get( service_constants.L3_ROUTER_NAT) context = kwargs['context'] @@ -508,7 +514,8 @@ def _notify_l3_agent_port_update(resource, event, trigger, **kwargs): } _notify_port_delete( event, resource, trigger, **removed_router_args) - return + if not n_utils.is_dvr_serviced(new_device_owner): + return _notify_l3_agent_new_port(resource, event, trigger, **kwargs) 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 c65b271c7..a6981f251 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 @@ -456,10 +456,13 @@ class L3DvrTestCase(ml2_test_base.ML2TestFramework): self.assertEqual(1, len(fixed_ips)) def test_update_vm_port_host_router_update(self): - # register l3 agent in dvr mode in addition to existing dvr_snat agent - HOST = 'host1' - dvr_agent = helpers.register_l3_agent( - host=HOST, agent_mode=constants.L3_AGENT_MODE_DVR) + # register l3 agents in dvr mode in addition to existing dvr_snat agent + HOST1 = 'host1' + dvr_agent1 = helpers.register_l3_agent( + host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR) + HOST2 = 'host2' + dvr_agent2 = helpers.register_l3_agent( + host=HOST2, agent_mode=constants.L3_AGENT_MODE_DVR) router = self._create_router() with self.subnet() as subnet: self.l3_plugin.add_router_interface( @@ -469,24 +472,48 @@ class L3DvrTestCase(ml2_test_base.ML2TestFramework): # since there are no vm ports on HOST, at this point the router # should be scheduled to only dvr_snat agent agents = self.l3_plugin.list_l3_agents_hosting_router( - self.context, router['id']) - self.assertEqual(1, len(agents['agents'])) - self.assertEqual(self.l3_agent['id'], agents['agents'][0]['id']) + self.context, router['id'])['agents'] + self.assertEqual(1, len(agents)) + self.assertEqual(self.l3_agent['id'], agents[0]['id']) with mock.patch.object(self.l3_plugin, '_l3_rpc_notifier') as l3_notifier,\ self.port(subnet=subnet, device_owner=DEVICE_OWNER_COMPUTE) as port: + self.l3_plugin.agent_notifiers[ + constants.AGENT_TYPE_L3] = l3_notifier self.core_plugin.update_port( self.context, port['port']['id'], - {'port': {'binding:host_id': HOST}}) + {'port': {'binding:host_id': HOST1}}) - # now router should be scheduled to both agents + # now router should be scheduled to dvr_snat agent and + # dvr agent on host1 agents = self.l3_plugin.list_l3_agents_hosting_router( - self.context, router['id']) - self.assertEqual(2, len(agents['agents'])) - self.assertIn(dvr_agent['id'], - [agent['id'] for agent in agents['agents']]) - # and notification should only be sent to the agent on HOST + self.context, router['id'])['agents'] + self.assertEqual(2, len(agents)) + self.assertIn(dvr_agent1['id'], + [agent['id'] for agent in agents]) + self.assertNotIn(dvr_agent2['id'], + [agent['id'] for agent in agents]) + # and notification should only be sent to the agent on host1 l3_notifier.routers_updated_on_host.assert_called_once_with( - self.context, {router['id']}, HOST) + self.context, {router['id']}, HOST1) self.assertFalse(l3_notifier.routers_updated.called) + + # updating port's host (instance migration) + l3_notifier.reset_mock() + self.core_plugin.update_port( + self.context, port['port']['id'], + {'port': {'binding:host_id': HOST2}}) + # now router should be scheduled to dvr_snat agent and + # dvr agent on host2 + agents = self.l3_plugin.list_l3_agents_hosting_router( + self.context, router['id'])['agents'] + self.assertEqual(2, len(agents)) + self.assertIn(dvr_agent2['id'], + [agent['id'] for agent in agents]) + self.assertNotIn(dvr_agent1['id'], + [agent['id'] for agent in agents]) + l3_notifier.routers_updated_on_host.assert_called_once_with( + self.context, {router['id']}, HOST2) + l3_notifier.router_removed_from_agent.assert_called_once_with( + self.context, router['id'], HOST1) -- 2.45.2