Skip to content

Commit 4c93b29

Browse files
scilla0531gotostack
authored andcommitted
Clean up db residual record from dvr port
Delete the DVR port record on the node when the router is deleted from the node. This patch fixes an issue where the DB actions have no context writer which will actually update nothing duing the following `delete_distributed_port_binding_if_stale`. As well as fixing that when the dvr router was deleted from a node, only the ml2_distributed_port_bindings were cleaned up and not the ml2_port_binding_levels records for the dvr port. Remove the last VM under one router in one host, the ml2_distributed_port_bindings and ml2_port_binding_levels will remain the record. So if VMs under one router had spread on many hosts, and router (router ports) still exists, even there is only one VM under it, the binding entries will still be equal to amount of hosts where this router resided before. The result is a large amount of redundant data in the database, even if some nodes are no longer in the cluster, there are still records for that node. Closes-Bug: #1976439 Change-Id: I320ac2306e0f25ff933d8271203e192486062d61 (cherry picked from commit ad3f7a8)
1 parent ca96555 commit 4c93b29

File tree

3 files changed

+72
-11
lines changed

3 files changed

+72
-11
lines changed

neutron/db/l3_dvrscheduler_db.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,9 @@ def _unbind_dvr_port_before_delete(
283283
int_ports = self._core_plugin.get_ports(
284284
context.elevated(), filters=filter_rtr)
285285
for port in int_ports:
286-
dvr_binding = (ml2_db.
287-
get_distributed_port_binding_by_host(
288-
context, port['id'], port_host))
289-
if dvr_binding:
290-
# unbind this port from router
291-
dvr_binding['router_id'] = None
292-
dvr_binding.update(dvr_binding)
286+
# unbind this port from router
287+
ml2_db.update_distributed_port_binding_by_host(
288+
context, port['id'], port_host, None)
293289

294290
def _get_active_l3_agent_routers_sync_data(self, context, host, agent,
295291
router_ids):

neutron/plugins/ml2/db.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,13 @@ def delete_distributed_port_binding_if_stale(context, binding):
120120
with db_api.CONTEXT_WRITER.using(context):
121121
LOG.debug("Distributed port: Deleting binding %s", binding)
122122
context.session.delete(binding)
123+
for bindlv in (context.session.query(models.PortBindingLevel).
124+
filter_by(port_id=binding.port_id, host=binding.host)):
125+
context.session.delete(bindlv)
126+
LOG.debug("For port %(port_id)s, host %(host)s, "
127+
"cleared binding levels",
128+
{'port_id': binding.port_id,
129+
'host': binding.host})
123130

124131

125132
def get_port(context, port_id):
@@ -249,6 +256,17 @@ def get_distributed_port_binding_by_host(context, port_id, host):
249256
return binding
250257

251258

259+
def update_distributed_port_binding_by_host(context, port_id, host, router_id):
260+
with db_api.CONTEXT_WRITER.using(context):
261+
bindings = (
262+
context.session.query(models.DistributedPortBinding).
263+
filter(models.DistributedPortBinding.port_id.startswith(port_id),
264+
models.DistributedPortBinding.host == host).all())
265+
for binding in bindings or []:
266+
binding['router_id'] = router_id or None
267+
binding.update(binding)
268+
269+
252270
def get_distributed_port_bindings(context, port_id):
253271
with db_api.CONTEXT_READER.using(context):
254272
bindings = (context.session.query(models.DistributedPortBinding).

neutron/tests/unit/plugins/ml2/test_db.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,26 @@ def _setup_distributed_binding(self, network_id,
375375
self.ctx.session.add(record)
376376
return record
377377

378+
def _setup_port_binding_level(self, network_id, port_id, host_id,
379+
level=0, driver='openvswitch'):
380+
with db_api.CONTEXT_WRITER.using(self.ctx):
381+
record = models.PortBindingLevel(
382+
port_id=port_id,
383+
host=host_id,
384+
level=level,
385+
driver=driver,
386+
segment_id=network_id)
387+
self.ctx.session.add(record)
388+
return record
389+
390+
def _setup_neutron_network_segment(self, segment_id, network_id):
391+
with db_api.CONTEXT_WRITER.using(self.ctx):
392+
segment = network_obj.NetworkSegment(
393+
self.ctx,
394+
id=segment_id, network_id=network_id,
395+
network_type=constants.TYPE_FLAT)
396+
segment.create()
397+
378398
def test_ensure_distributed_port_binding_deals_with_db_duplicate(self):
379399
network_id = uuidutils.generate_uuid()
380400
port_id = uuidutils.generate_uuid()
@@ -418,17 +438,44 @@ def test_ensure_distributed_port_binding_multiple_bindings(self):
418438

419439
def test_delete_distributed_port_binding_if_stale(self):
420440
network_id = uuidutils.generate_uuid()
441+
segment_id = uuidutils.generate_uuid()
421442
port_id = uuidutils.generate_uuid()
443+
host_id = 'foo_host_id'
422444
self._setup_neutron_network(network_id, [port_id])
445+
self._setup_neutron_network_segment(segment_id, network_id)
446+
423447
binding = self._setup_distributed_binding(
424-
network_id, port_id, None, 'foo_host_id')
448+
network_id, port_id, None, host_id)
449+
binding_levels = self._setup_port_binding_level(
450+
segment_id, port_id, host_id)
425451

426-
ml2_db.delete_distributed_port_binding_if_stale(self.ctx,
427-
binding)
452+
ml2_db.delete_distributed_port_binding_if_stale(
453+
self.ctx, binding)
428454
count = (self.ctx.session.query(models.DistributedPortBinding).
429-
filter_by(port_id=binding.port_id).count())
455+
filter_by(port_id=binding.port_id, host=host_id).count())
430456
self.assertFalse(count)
431457

458+
count = (self.ctx.session.query(models.PortBindingLevel).
459+
filter_by(port_id=binding_levels.port_id,
460+
host=host_id).count())
461+
self.assertFalse(count)
462+
463+
def test_update_distributed_port_binding_by_host(self):
464+
network_id = uuidutils.generate_uuid()
465+
router_id = uuidutils.generate_uuid()
466+
port_id = uuidutils.generate_uuid()
467+
host_id = 'foo_host_id'
468+
self._setup_neutron_network(network_id, [port_id])
469+
470+
binding = self._setup_distributed_binding(
471+
network_id, port_id, router_id, host_id)
472+
473+
ml2_db.update_distributed_port_binding_by_host(
474+
self.ctx, binding.port_id, host_id, None)
475+
binding = (self.ctx.session.query(models.DistributedPortBinding).
476+
filter_by(port_id=port_id, host=host_id).one())
477+
self.assertFalse(binding.router_id)
478+
432479
def test_get_distributed_port_binding_by_host_not_found(self):
433480
port = ml2_db.get_distributed_port_binding_by_host(
434481
self.ctx, 'foo_port_id', 'foo_host_id')

0 commit comments

Comments
 (0)