From b1b0937eb42442ae6f585eba477189321ef08c80 Mon Sep 17 00:00:00 2001 From: Oleg Bondarev Date: Tue, 11 Jun 2019 12:22:14 +0400 Subject: [PATCH] DVR: on new port only send router update on port's host When new DVR serviceable port appears on new node we need to update node's l3 agent with all routers which have the port's subnets, including connected routers. We don't need to update all nodes hosting these routers. It costs us much as all l3 agents then go back to neutron server and request routers info for no good reason. This was one of the main issues with DVR at scale fixed in Mitaka. Change-Id: I99d01d7bf29f236eff0f80d1ae8659f64ac55d39 Related-Bug: #1830456 (cherry picked from commit 52529bc949acff9a3454abd44925342468064b78) --- neutron/db/l3_dvrscheduler_db.py | 30 +++++++------------ .../l3_router/test_l3_dvr_router_plugin.py | 4 +-- .../unit/scheduler/test_l3_agent_scheduler.py | 15 +++------- 3 files changed, 15 insertions(+), 34 deletions(-) diff --git a/neutron/db/l3_dvrscheduler_db.py b/neutron/db/l3_dvrscheduler_db.py index a60a3c64912..44c1f90f83a 100644 --- a/neutron/db/l3_dvrscheduler_db.py +++ b/neutron/db/l3_dvrscheduler_db.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import collections - from neutron_lib.api.definitions import portbindings from neutron_lib.api import extensions from neutron_lib.callbacks import events @@ -133,26 +131,18 @@ class L3_DVRsch_db_mixin(l3agent_sch_db.L3AgentSchedulerDbMixin): if agent.host == port_host: agent_port_host_match = True if not agent_port_host_match: - hosts = set([port_host]) + connected_router_ids = set(router_ids) for router_id in router_ids: - hosts |= set(self.get_hosts_to_notify(context, router_id)) + connected_router_ids.update( + self._get_other_dvr_router_ids_connected_router( + context, router_id)) - host_routers = collections.defaultdict(set) - for router_id in router_ids: - # avoid calling get_ports in host loop - subnet_ids = self.get_subnet_ids_on_router( - context.elevated(), router_id) - for host in hosts: - LOG.debug('DVR: Handle new service port, host %(host)s, ' - 'router ids %(router_id)s', - {'host': host, 'router_id': router_id}) - if self._check_dvr_serviceable_ports_on_host( - context.elevated(), host, subnet_ids): - host_routers[host].add(router_id) - - for host, router_ids in host_routers.items(): - self.l3_rpc_notifier.routers_updated_on_host( - context, router_ids, host) + LOG.debug('DVR: Handle new service port, host %(host)s, ' + 'router ids %(router_ids)s', + {'host': port_host, + 'router_ids': connected_router_ids}) + self.l3_rpc_notifier.routers_updated_on_host( + context, connected_router_ids, port_host) def get_dvr_snat_agent_list(self, context): agent_filters = {'agent_modes': [n_const.L3_AGENT_MODE_DVR_SNAT]} 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 3d40c518635..f90c74ed4d6 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 @@ -768,7 +768,6 @@ class L3DvrTestCase(L3DvrTestCaseBase): expected_routers_updated_calls = [ mock.call(self.context, mock.ANY, 'host0'), mock.call(self.context, mock.ANY, HOST1), - mock.call(self.context, mock.ANY, HOST1), mock.call(self.context, mock.ANY, HOST2)] l3_notifier.routers_updated_on_host.assert_has_calls( expected_routers_updated_calls, any_order=True) @@ -1090,7 +1089,7 @@ class L3DvrTestCase(L3DvrTestCaseBase): {'port': { 'allowed_address_pairs': allowed_address_pairs}}) self.assertEqual( - 3, l3_notifier.routers_updated_on_host.call_count) + 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( @@ -1140,7 +1139,6 @@ class L3DvrTestCase(L3DvrTestCaseBase): expected_routers_updated_calls = [ mock.call(self.context, mock.ANY, HOST1), mock.call(self.context, mock.ANY, HOST2), - 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, any_order=True) diff --git a/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py b/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py index 4c9b61fefca..78b574f7ea1 100644 --- a/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py +++ b/neutron/tests/unit/scheduler/test_l3_agent_scheduler.py @@ -1261,13 +1261,7 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, '.L3AgentNotifyAPI'),\ mock.patch.object( self.dut, 'get_l3_agents', - return_value=[agent_on_host]) as get_l3_agents,\ - mock.patch.object( - self.dut, 'get_hosts_to_notify', - return_value=['other_host', 'host1']),\ - mock.patch.object( - self.dut, '_check_dvr_serviceable_ports_on_host', - return_value=True): + return_value=[agent_on_host]) as get_l3_agents: self.dut.dvr_handle_new_service_port( self.adminContext, port) @@ -1275,10 +1269,9 @@ class L3DvrSchedulerTestCase(L3SchedulerBaseMixin, get_l3_agents.assert_called_once_with( self.adminContext, filters={'host': [port[portbindings.HOST_ID]]}) - self.dut.l3_rpc_notifier.routers_updated_on_host.assert_has_calls( - [mock.call(self.adminContext, {'r1', 'r2'}, 'host1'), - mock.call(self.adminContext, {'r1', 'r2'}, 'other_host')], - any_order=True) + self.dut.l3_rpc_notifier.routers_updated_on_host.\ + assert_called_once_with(self.adminContext, + {'r1', 'r2'}, 'host1') self.assertFalse(self.dut.l3_rpc_notifier.routers_updated.called) def test_get_dvr_routers_by_subnet_ids(self):