Skip to content

Commit f6c3552

Browse files
committed
[ovn] Stop monitoring the SB MAC_Binding table to reduce mem footprint
The MAC_Binding table in the SB database may grow indefinitely (due to a lack of an aging mechanism of its entries) and eventually lead to OOM killers for neutron-server which maintains an in-memory copy of the database. In order to stop monitoring this table, this patch is invoking the ovsdb-client tool to remove the entries associated to Floating IPs that have just been detached. The execution of this tool is really fast as it will just invoke a JSON-RPC transact command which doesn't require downloading the database contents. In a scale test, the memory consumption of neutron-server dropped from 75GB to 7GB with this patch. Closes-Bug: #1946318 Signed-off-by: Daniel Alvarez Sanchez <dalvarez@redhat.com> Change-Id: Id84bf17953527c415d611bfc198038fb6f811de3
1 parent dfcbb4c commit f6c3552

4 files changed

Lines changed: 63 additions & 23 deletions

File tree

neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from neutron_lib.exceptions import availability_zone as az_exc
3535
from neutron_lib.plugins import directory
3636
from neutron_lib.plugins.ml2 import api
37+
from oslo_concurrency import processutils
3738
from oslo_config import cfg
3839
from oslo_db import exception as os_db_exc
3940
from oslo_log import log
@@ -1098,10 +1099,18 @@ def set_port_status_down(self, port_id):
10981099

10991100
def delete_mac_binding_entries(self, external_ip):
11001101
"""Delete all MAC_Binding entries associated to this IP address"""
1101-
mac_binds = self.sb_ovn.db_find_rows(
1102-
'MAC_Binding', ('ip', '=', external_ip)).execute() or []
1103-
for entry in mac_binds:
1104-
self.sb_ovn.db_destroy('MAC_Binding', entry.uuid).execute()
1102+
cmd = ['ovsdb-client', 'transact', ovn_conf.get_ovn_sb_connection()]
1103+
1104+
if ovn_conf.get_ovn_sb_private_key():
1105+
cmd += ['-p', ovn_conf.get_ovn_sb_private_key(), '-c',
1106+
ovn_conf.get_ovn_sb_certificate(), '-C',
1107+
ovn_conf.get_ovn_sb_ca_cert()]
1108+
1109+
cmd += ['["OVN_Southbound", {"op": "delete", "table": "MAC_Binding", '
1110+
'"where": [["ip", "==", "%s"]]}]' % external_ip]
1111+
1112+
return processutils.execute(*cmd,
1113+
log_errors=processutils.LOG_FINAL_ERROR)
11051114

11061115
def update_segment_host_mapping(self, host, phy_nets):
11071116
"""Update SegmentHostMapping in DB"""

neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,6 @@ def from_server(cls, connection_string, helper, driver):
736736
helper.register_table('Encap')
737737
helper.register_table('Port_Binding')
738738
helper.register_table('Datapath_Binding')
739-
helper.register_table('MAC_Binding')
740739
helper.register_table('Connection')
741740
helper.register_columns('SB_Global', ['external_ids'])
742741
return cls(driver, connection_string, helper)

neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovsdb_monitor.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from unittest import mock
1616

1717
import fixtures as og_fixtures
18+
from oslo_concurrency import processutils
1819
from oslo_utils import uuidutils
1920

2021
from neutron.common.ovn import constants as ovn_const
@@ -33,17 +34,6 @@
3334
from ovsdbapp.backend.ovs_idl import idlutils
3435

3536

36-
class WaitForMACBindingDeleteEvent(event.WaitEvent):
37-
event_name = 'WaitForMACBindingDeleteEvent'
38-
39-
def __init__(self, entry):
40-
table = 'MAC_Binding'
41-
events = (self.ROW_DELETE,)
42-
conditions = (('_uuid', '=', entry),)
43-
super(WaitForMACBindingDeleteEvent, self).__init__(
44-
events, table, conditions, timeout=15)
45-
46-
4737
class WaitForDataPathBindingCreateEvent(event.WaitEvent):
4838
event_name = 'WaitForDataPathBindingCreateEvent'
4939

@@ -132,6 +122,22 @@ def _create_fip(self, port, fip_address):
132122
'port_id': port['id']}})
133123
return r1_f2
134124

125+
def _check_mac_binding_exists(self, macb_id):
126+
cmd = ['ovsdb-client', 'transact',
127+
self.mech_driver.sb_ovn.connection_string]
128+
129+
if self._ovsdb_protocol == 'ssl':
130+
cmd += ['-p', self.ovsdb_server_mgr.private_key, '-c',
131+
self.ovsdb_server_mgr.certificate, '-C',
132+
self.ovsdb_server_mgr.ca_cert]
133+
134+
cmd += ['["OVN_Southbound", {"op": "select", "table": "MAC_Binding", '
135+
'"where": [["_uuid", "==", ["uuid", "%s"]]]}]' % macb_id]
136+
137+
out, _ = processutils.execute(*cmd,
138+
log_errors=False)
139+
return str(macb_id) in out
140+
135141
def test_floatingip_mac_bindings(self):
136142
"""Check that MAC_Binding entries are cleared on FIP add/removal
137143
@@ -146,6 +152,8 @@ def test_floatingip_mac_bindings(self):
146152
* Check that the MAC_Binding entry gets deleted.
147153
"""
148154
net_name = 'network1'
155+
self.mech_driver.sb_ovn.idl.update_tables(
156+
['MAC_Binding'], self.mech_driver.sb_schema_helper.schema_json)
149157
row_event = WaitForDataPathBindingCreateEvent(net_name)
150158
self.mech_driver.sb_ovn.idl.notify_handler.watch_event(row_event)
151159
self._make_network(self.fmt, net_name, True)
@@ -158,22 +166,21 @@ def test_floatingip_mac_bindings(self):
158166
port = self.create_port()
159167

160168
# Ensure that the MAC_Binding entry gets deleted after creating a FIP
161-
row_event = WaitForMACBindingDeleteEvent(macb_id)
162-
self.mech_driver.sb_ovn.idl.notify_handler.watch_event(row_event)
163169
fip = self._create_fip(port, '100.0.0.21')
164-
self.assertTrue(row_event.wait())
170+
n_utils.wait_until_true(
171+
lambda: not self._check_mac_binding_exists(macb_id),
172+
timeout=15, sleep=1)
165173

166174
# Now that the FIP is created, add a new MAC_Binding entry with the
167175
# same IP address
168-
169176
macb_id = self.sb_api.db_create('MAC_Binding', datapath=dp[0]['_uuid'],
170177
ip='100.0.0.21').execute()
171178

172179
# Ensure that the MAC_Binding entry gets deleted after deleting the FIP
173-
row_event = WaitForMACBindingDeleteEvent(macb_id)
174-
self.mech_driver.sb_ovn.idl.notify_handler.watch_event(row_event)
175180
self.l3_plugin.delete_floatingip(self.context, fip['id'])
176-
self.assertTrue(row_event.wait())
181+
n_utils.wait_until_true(
182+
lambda: not self._check_mac_binding_exists(macb_id),
183+
timeout=15, sleep=1)
177184

178185
def _test_port_binding_and_status(self, port_id, action, status):
179186
# This function binds or unbinds port to chassis and

neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import copy
1616
import datetime
17+
import shlex
1718
from unittest import mock
1819
import uuid
1920

@@ -32,6 +33,7 @@
3233
from neutron_lib.plugins import directory
3334
from neutron_lib.tests import tools
3435
from neutron_lib.utils import net as n_net
36+
from oslo_concurrency import processutils
3537
from oslo_config import cfg
3638
from oslo_db import exception as os_db_exc
3739
from oslo_serialization import jsonutils
@@ -131,6 +133,29 @@ def setUp(self):
131133
p.start()
132134
self.addCleanup(p.stop)
133135

136+
def test_delete_mac_binding_entries(self):
137+
self.config(group='ovn', ovn_sb_private_key=None)
138+
expected = ('ovsdb-client transact tcp:127.0.0.1:6642 '
139+
'\'["OVN_Southbound", {"op": "delete", "table": '
140+
'"MAC_Binding", "where": [["ip", "==", "1.1.1.1"]]}]\'')
141+
with mock.patch.object(processutils, 'execute') as mock_execute:
142+
self.mech_driver.delete_mac_binding_entries('1.1.1.1')
143+
mock_execute.assert_called_once_with(*shlex.split(expected),
144+
log_errors=processutils.LOG_FINAL_ERROR)
145+
146+
def test_delete_mac_binding_entries_ssl(self):
147+
self.config(group='ovn', ovn_sb_private_key='pk')
148+
self.config(group='ovn', ovn_sb_certificate='cert')
149+
self.config(group='ovn', ovn_sb_ca_cert='ca')
150+
expected = ('ovsdb-client transact tcp:127.0.0.1:6642 '
151+
'-p pk -c cert -C ca '
152+
'\'["OVN_Southbound", {"op": "delete", "table": '
153+
'"MAC_Binding", "where": [["ip", "==", "1.1.1.1"]]}]\'')
154+
with mock.patch.object(processutils, 'execute') as mock_execute:
155+
self.mech_driver.delete_mac_binding_entries('1.1.1.1')
156+
mock_execute.assert_called_once_with(*shlex.split(expected),
157+
log_errors=processutils.LOG_FINAL_ERROR)
158+
134159

135160
class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
136161
@mock.patch.object(ovsdb_monitor.OvnInitPGNbIdl, 'from_server')

0 commit comments

Comments
 (0)