From: Erik Colnick Date: Fri, 23 Jan 2015 19:16:28 +0000 (-0700) Subject: Improve DVR scale performance X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=b96a22661290ce2ea747537512eab2fb767679e6;p=openstack-build%2Fneutron-build.git Improve DVR scale performance Only process floating ips on a router that are relevant to the agent hosting the router (don't process floating ips assigned to a router if the associated vm is not hosted on the compute node requesting the router sync). In this way, the number of database calls made during the DVR router updates is optimized to eliminate unnecessary duplication of calls which return the same data or are made to get data for routers which are not relevant to the sync_routers request from the agent. Change-Id: I4e8477bb61ffff164d2f3bbebb94e95a25838ce0 Partial-Bug: #1413314 --- diff --git a/neutron/db/l3_dvr_db.py b/neutron/db/l3_dvr_db.py index d98c574ad..2912744bd 100644 --- a/neutron/db/l3_dvr_db.py +++ b/neutron/db/l3_dvr_db.py @@ -414,30 +414,26 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin, router[SNAT_ROUTER_INTF_KEY] = snat_router_intfs return routers_dict - def _process_floating_ips(self, context, routers_dict, floating_ips): + def _process_floating_ips_dvr(self, context, routers_dict, + floating_ips, host, agent): + fip_sync_interfaces = None + LOG.debug("FIP Agent : %s ", agent.id) for floating_ip in floating_ips: router = routers_dict.get(floating_ip['router_id']) if router: router_floatingips = router.get(l3_const.FLOATINGIP_KEY, []) - floatingip_agent_intfs = [] if router['distributed']: - floating_ip['host'] = self.get_vm_port_hostid( - context, floating_ip['port_id']) - LOG.debug("Floating IP host: %s", floating_ip['host']) - # if no VM there won't be an agent assigned - if not floating_ip['host']: + if floating_ip.get('host', None) != host: continue - fip_agent = self._get_agent_by_type_and_host( - context, l3_const.AGENT_TYPE_L3, - floating_ip['host']) - LOG.debug("FIP Agent : %s ", fip_agent['id']) - floatingip_agent_intfs = self.get_fip_sync_interfaces( - context, fip_agent['id']) - LOG.debug("FIP Agent ports: %s", floatingip_agent_intfs) + LOG.debug("Floating IP host: %s", floating_ip['host']) router_floatingips.append(floating_ip) router[l3_const.FLOATINGIP_KEY] = router_floatingips + if not fip_sync_interfaces: + fip_sync_interfaces = self.get_fip_sync_interfaces( + context, agent.id) + LOG.debug("FIP Agent ports: %s", fip_sync_interfaces) router[l3_const.FLOATINGIP_AGENT_INTF_KEY] = ( - floatingip_agent_intfs) + fip_sync_interfaces) def get_fip_sync_interfaces(self, context, fip_agent_id): """Query router interfaces that relate to list of router_ids.""" @@ -451,15 +447,23 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin, self._populate_subnet_for_ports(context, interfaces) return interfaces - def get_sync_data(self, context, router_ids=None, active=None): + def get_dvr_sync_data(self, context, host, agent, router_ids=None, + active=None): routers, interfaces, floating_ips = self._get_router_info_list( context, router_ids=router_ids, active=active, device_owners=l3_const.ROUTER_INTERFACE_OWNERS) + port_filter = {portbindings.HOST_ID: [host]} + ports = self._core_plugin.get_ports(context, port_filter) + port_dict = dict((port['id'], port) for port in ports) # Add the port binding host to the floatingip dictionary for fip in floating_ips: - fip['host'] = self.get_vm_port_hostid(context, fip['port_id']) + vm_port = port_dict.get(fip['port_id'], None) + if vm_port: + fip['host'] = self.get_vm_port_hostid(context, fip['port_id'], + port=vm_port) routers_dict = self._process_routers(context, routers) - self._process_floating_ips(context, routers_dict, floating_ips) + self._process_floating_ips_dvr(context, routers_dict, + floating_ips, host, agent) self._process_interfaces(routers_dict, interfaces) return routers_dict.values() diff --git a/neutron/db/l3_dvrscheduler_db.py b/neutron/db/l3_dvrscheduler_db.py index e67c97f63..5592beaf6 100644 --- a/neutron/db/l3_dvrscheduler_db.py +++ b/neutron/db/l3_dvrscheduler_db.py @@ -315,6 +315,15 @@ class L3_DVRsch_db_mixin(l3agent_sch_db.L3AgentSchedulerDbMixin): context, router_id, chosen_agent) return chosen_agent + def _get_active_l3_agent_routers_sync_data(self, context, host, agent, + router_ids): + if n_utils.is_extension_supported(self, q_const.L3_HA_MODE_EXT_ALIAS): + return self.get_ha_sync_data_for_host(context, host, + router_ids=router_ids, + active=True) + return self.get_dvr_sync_data(context, host, agent, + router_ids=router_ids, active=True) + def _notify_l3_agent_new_port(resource, event, trigger, **kwargs): LOG.debug('Received %s %s' % (resource, event)) diff --git a/neutron/db/l3_hamode_db.py b/neutron/db/l3_hamode_db.py index 1c0a6e595..3f52e0a5e 100644 --- a/neutron/db/l3_hamode_db.py +++ b/neutron/db/l3_hamode_db.py @@ -23,6 +23,7 @@ from sqlalchemy import orm from neutron.api.v2 import attributes from neutron.common import constants +from neutron.common import utils as n_utils from neutron.db import agents_db from neutron.db import l3_dvr_db from neutron.db import model_base @@ -458,7 +459,15 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin): def get_ha_sync_data_for_host(self, context, host=None, router_ids=None, active=None): - sync_data = super(L3_HA_NAT_db_mixin, self).get_sync_data(context, - router_ids, - active) + if n_utils.is_extension_supported(self, + constants.L3_DISTRIBUTED_EXT_ALIAS): + # DVR has to be handled differently + agent = self._get_agent_by_type_and_host(context, + constants.AGENT_TYPE_L3, + host) + sync_data = self.get_dvr_sync_data(context, host, agent, + router_ids, active) + else: + sync_data = super(L3_HA_NAT_db_mixin, self).get_sync_data(context, + router_ids, active) return self._process_sync_ha_data(context, sync_data, host) diff --git a/neutron/tests/unit/db/test_l3_dvr_db.py b/neutron/tests/unit/db/test_l3_dvr_db.py index 9f6dc0170..d9ec10463 100644 --- a/neutron/tests/unit/db/test_l3_dvr_db.py +++ b/neutron/tests/unit/db/test_l3_dvr_db.py @@ -317,8 +317,11 @@ class L3DvrTestCase(testlib_api.SqlTestCase): floatingip = { 'id': _uuid(), 'port_id': _uuid(), - 'router_id': 'foo_router_id' + 'router_id': 'foo_router_id', + 'host': hostid } + if not hostid: + hostid = 'not_my_host_id' routers = { 'foo_router_id': router } @@ -334,25 +337,22 @@ class L3DvrTestCase(testlib_api.SqlTestCase): return_value=fipagent) self.mixin.get_fip_sync_interfaces = mock.Mock( return_value='fip_interface') + agent = mock.Mock() + agent.id = fipagent['id'] - self.mixin._process_floating_ips(self.ctx, routers, [floatingip]) + self.mixin._process_floating_ips_dvr(self.ctx, routers, [floatingip], + hostid, agent) return (router, floatingip) - def test_floatingip_on_port_no_host(self): + def test_floatingip_on_port_not_host(self): router, fip = self._floatingip_on_port_test_setup(None) - self.assertTrue(self.mixin.get_vm_port_hostid.called) - self.assertFalse(self.mixin._get_agent_by_type_and_host.called) - self.assertFalse(self.mixin.get_fip_sync_interfaces.called) - self.assertNotIn(l3_const.FLOATINGIP_KEY, router) self.assertNotIn(l3_const.FLOATINGIP_AGENT_INTF_KEY, router) def test_floatingip_on_port_with_host(self): router, fip = self._floatingip_on_port_test_setup(_uuid()) - self.assertTrue(self.mixin.get_vm_port_hostid.called) - self.assertTrue(self.mixin._get_agent_by_type_and_host.called) self.assertTrue(self.mixin.get_fip_sync_interfaces.called) self.assertIn(l3_const.FLOATINGIP_KEY, router)