Skip to content

Commit fd06c73

Browse files
adamoswickbrianphaley
authored andcommitted
For hosts in DVR mode, only fetch bound FIPs
Currently, agents in DVR mode requesting a router update fetch all the FIPs on a network from the DB rather than just the FIPs that are relevant to the specific host requesting the update. While not noticable in smaller networks with a limited number of floating IPs, this can add significant overhead in larger networks with many FIPs and hosts. That overhead comes from Python mapping the responses from the DB into objects, making extra DB calls per FIP returned and adding additional iterations to the loop in _get_dvr_sync_data. These objects are mostly discarded later on and not updated nor included in the RPC response. This change ensures that we only fetch FIPs from the DB that are bound to the host requesting the update or those which are in a pre-live migration state (as they may be migrated to the host in question). Closes-Bug: #2028185 Change-Id: I199b0b1456aa15dadcc24cafc89db1072d224efd (cherry picked from commit 96fd203)
1 parent 37bdb9e commit fd06c73

File tree

4 files changed

+50
-9
lines changed

4 files changed

+50
-9
lines changed

neutron/db/l3_db.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,7 +1889,7 @@ def _make_floatingip_dict_with_scope(self, floatingip_obj, scope_id):
18891889
d['fixed_ip_address_scope'] = scope_id
18901890
return d
18911891

1892-
def _get_sync_floating_ips(self, context, router_ids):
1892+
def _get_sync_floating_ips(self, context, router_ids, host=None):
18931893
"""Query floating_ips that relate to list of router_ids with scope.
18941894
18951895
This is different than the regular get_floatingips in that it finds the
@@ -1905,7 +1905,7 @@ def _get_sync_floating_ips(self, context, router_ids):
19051905
return [
19061906
self._make_floatingip_dict_with_scope(*scoped_fip)
19071907
for scoped_fip in l3_obj.FloatingIP.get_scoped_floating_ips(
1908-
context, router_ids)
1908+
context, router_ids, host)
19091909
]
19101910

19111911
def _get_sync_interfaces(self, context, router_ids, device_owners=None):
@@ -2055,7 +2055,7 @@ def _process_interfaces(self, routers_dict, interfaces):
20552055
router[constants.INTERFACE_KEY] = router_interfaces
20562056

20572057
def _get_router_info_list(self, context, router_ids=None, active=None,
2058-
device_owners=None):
2058+
device_owners=None, fip_host_filter=None):
20592059
"""Query routers and their related floating_ips, interfaces."""
20602060
with db_api.CONTEXT_WRITER.using(context):
20612061
routers = self._get_sync_routers(context,
@@ -2064,7 +2064,8 @@ def _get_router_info_list(self, context, router_ids=None, active=None,
20642064
router_ids = [router['id'] for router in routers]
20652065
interfaces = self._get_sync_interfaces(
20662066
context, router_ids, device_owners)
2067-
floating_ips = self._get_sync_floating_ips(context, router_ids)
2067+
floating_ips = self._get_sync_floating_ips(
2068+
context, router_ids, host=fip_host_filter)
20682069
return (routers, interfaces, floating_ips)
20692070

20702071
def get_sync_data(self, context, router_ids=None, active=None):

neutron/db/l3_dvr_db.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -979,9 +979,17 @@ def _get_fip_agent_gw_ports(self, context, fip_agent_id):
979979
@log_helper.log_method_call
980980
def _get_dvr_sync_data(self, context, host, agent, router_ids=None,
981981
active=None):
982+
# If the requesting agent is in normal dvr mode, we can fetch
983+
# only FIPs bound to the particular host requesting the update
984+
requesting_agent_mode = self._get_agent_mode(agent)
985+
fip_host_filter = None
986+
if requesting_agent_mode == const.L3_AGENT_MODE_DVR:
987+
fip_host_filter = host
988+
982989
routers, interfaces, floating_ips = self._get_router_info_list(
983990
context, router_ids=router_ids, active=active,
984-
device_owners=const.ROUTER_INTERFACE_OWNERS)
991+
device_owners=const.ROUTER_INTERFACE_OWNERS,
992+
fip_host_filter=fip_host_filter)
985993
dvr_router_ids = set(router['id'] for router in routers
986994
if is_distributed_router(router))
987995
floating_ip_port_ids = [fip['port_id'] for fip in floating_ips
@@ -1014,7 +1022,6 @@ def _get_dvr_sync_data(self, context, host, agent, router_ids=None,
10141022
if len(l3_agent_on_host):
10151023
l3_agent_mode = self._get_agent_mode(
10161024
l3_agent_on_host[0])
1017-
requesting_agent_mode = self._get_agent_mode(agent)
10181025
# Consider the ports where the portbinding host and
10191026
# request host match.
10201027
if port_host == host:

neutron/objects/router.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from oslo_utils import versionutils
2222
from oslo_versionedobjects import fields as obj_fields
2323
from sqlalchemy import func
24+
from sqlalchemy import or_
2425
from sqlalchemy import sql
2526

2627
from neutron.db.models import dvr as dvr_models
@@ -30,6 +31,7 @@
3031
from neutron.db import models_v2
3132
from neutron.objects import base
3233
from neutron.objects.qos import binding as qos_binding
34+
from neutron.plugins.ml2 import models as ml2_models
3335

3436

3537
@base.NeutronObjectRegistry.register
@@ -390,7 +392,7 @@ def obj_make_compatible(self, primitive, target_version):
390392

391393
@classmethod
392394
@db_api.CONTEXT_READER
393-
def get_scoped_floating_ips(cls, context, router_ids):
395+
def get_scoped_floating_ips(cls, context, router_ids, host=None):
394396
query = context.session.query(l3.FloatingIP,
395397
models_v2.SubnetPool.address_scope_id)
396398
query = query.join(
@@ -405,6 +407,19 @@ def get_scoped_floating_ips(cls, context, router_ids):
405407
models_v2.SubnetPool,
406408
models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id)
407409

410+
# If a host value is provided, filter output to a specific host
411+
if host is not None:
412+
query = query.outerjoin(
413+
ml2_models.PortBinding,
414+
models_v2.Port.id == ml2_models.PortBinding.port_id)
415+
# Also filter for ports with migrating_to as they may be relevant
416+
# to this host but might not yet have the 'host' column updated
417+
# if the migration is in a pre-live migration state
418+
query = query.filter(or_(
419+
ml2_models.PortBinding.host == host,
420+
ml2_models.PortBinding.profile.like('%migrating_to%'),
421+
))
422+
408423
# Filter out on router_ids
409424
query = query.filter(l3.FloatingIP.router_id.in_(router_ids))
410425

neutron/tests/unit/objects/test_router.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717

1818
import netaddr
1919

20+
from neutron_lib.api.definitions import portbindings
2021
from neutron_lib import constants
2122
from neutron_lib.db import api as db_api
2223
from oslo_utils import uuidutils
2324

2425
from neutron.db import l3_attrs_db
26+
from neutron.objects import ports
2527
from neutron.objects.qos import binding as qos_binding
2628
from neutron.objects.qos import policy
2729
from neutron.objects import router
@@ -298,10 +300,10 @@ def test_v1_2_to_v1_1_drops_qos_network_policy_id(self):
298300
obj_v1_1['versioned_object.data'])
299301

300302
def test_get_scoped_floating_ips(self):
301-
def compare_results(router_ids, original_fips):
303+
def compare_results(router_ids, original_fips, host=None):
302304
fips_scope = [fip for fip in
303305
router.FloatingIP.get_scoped_floating_ips(
304-
self.context, router_ids)]
306+
self.context, router_ids, host=host)]
305307
fip_ids = [fip[0].id for fip in fips_scope]
306308
as_ids = {fip[1] for fip in fips_scope}
307309
self.assertCountEqual(original_fips, fip_ids)
@@ -336,11 +338,27 @@ def compare_results(router_ids, original_fips):
336338
fip.create()
337339
routers[router_id].append(fip.id)
338340

341+
# Associate port with a host
342+
port_binding = ports.PortBinding(
343+
self.context,
344+
port_id=fip.fixed_port_id,
345+
host=f"compute{j}",
346+
vif_type=portbindings.VIF_TYPE_OTHER,
347+
)
348+
port_binding.create()
349+
339350
# For each router we created, fetch the fips and ensure the
340351
# results match what we originally created
341352
for router_id, original_fips in routers.items():
342353
compare_results([router_id], original_fips)
343354

355+
# Fetch the first FIP in each router as we can assume that this is
356+
# bound to compute0, then attempt to filter by this compute host
357+
host_filtered_fips = []
358+
for router_id, original_fips in routers.items():
359+
host_filtered_fips.append(original_fips[1])
360+
compare_results(routers.keys(), host_filtered_fips, host="compute1")
361+
344362
# Now try to fetch all the fips for all the routers at once
345363
original_fips = list(chain.from_iterable(routers.values()))
346364
compare_results(routers.keys(), original_fips)

0 commit comments

Comments
 (0)