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 52529bc949)
This commit is contained in:
Oleg Bondarev 2019-06-11 12:22:14 +04:00 committed by norman shen
parent 7037a7dd6f
commit 05d6f0892f
3 changed files with 15 additions and 34 deletions

View File

@ -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.callbacks import events
from neutron_lib.callbacks import registry
@ -131,26 +129,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]}

View File

@ -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)

View File

@ -1231,13 +1231,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)
@ -1245,10 +1239,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):