Skip to content

Commit 46aefeb

Browse files
committed
[OVN] Update ovn meter when neutron server reloads
Up until now, we needed to remove all logging objects to see the meter-band properties being changed after a server restart. Now we check for inconsistencies between the neutron configuration and the OVN meter-band object after a restart. The function create_ovn_fair_meter is now located in the ovn_driver instead of the log_driver so as to be able to call it from the maintenance task. Conflicts: neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_client.py Closes-bug: #2017145 Signed-off-by: Elvira García <[email protected]> Change-Id: I24cef85ed68c893a740445707f88296d763c8de8 (cherry picked from commit c3602ac)
1 parent 3b65c1a commit 46aefeb

File tree

6 files changed

+181
-102
lines changed

6 files changed

+181
-102
lines changed

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from neutron.db import segments_db
4343
from neutron.objects import router as router_obj
4444
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_db_sync
45+
from neutron.services.logapi.drivers.ovn import driver as log_driver
4546

4647

4748
CONF = cfg.CONF
@@ -978,6 +979,24 @@ def create_router_extra_attributes_registers(self):
978979

979980
raise periodics.NeverAgain()
980981

982+
@periodics.periodic(spacing=600, run_immediately=True)
983+
def check_fair_meter_consistency(self):
984+
"""Update the logging meter after neutron-server reload
985+
986+
When we change the rate and burst limit we need to update the fair
987+
meter band to apply the new values. This is called from the ML2/OVN
988+
driver after the OVN NB idl is loaded
989+
990+
"""
991+
if not self.has_lock:
992+
return
993+
if log_driver.OVNDriver.network_logging_supported(self._nb_idl):
994+
meter_name = (
995+
cfg.CONF.network_log.local_output_log_base or "acl_log_meter")
996+
self._ovn_client.create_ovn_fair_meter(meter_name,
997+
from_reload=True)
998+
raise periodics.NeverAgain()
999+
9811000

9821001
class HashRingHealthCheckPeriodics(object):
9831002

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from neutron_lib.plugins import constants as plugin_constants
3232
from neutron_lib.plugins import directory
3333
from neutron_lib.plugins import utils as p_utils
34+
from neutron_lib.services.logapi import constants as log_const
3435
from neutron_lib.utils import helpers
3536
from neutron_lib.utils import net as n_net
3637
from oslo_config import cfg
@@ -2639,3 +2640,54 @@ def add_txns_to_remove_port_dns_records(self, txn, port):
26392640
if ls_dns_record.records.get(ptr_record):
26402641
txn.add(self._nb_idl.dns_remove_record(
26412642
ls_dns_record.uuid, ptr_record, if_exists=True))
2643+
2644+
def create_ovn_fair_meter(self, meter_name, from_reload=False, txn=None):
2645+
"""Create row in Meter table with fair attribute set to True.
2646+
2647+
Create a row in OVN's NB Meter table based on well-known name. This
2648+
method uses the network_log configuration to specify the attributes
2649+
of the meter. Current implementation needs only one 'fair' meter row
2650+
which is then referred by multiple ACL rows.
2651+
2652+
:param meter_name: ovn northbound meter name.
2653+
:param from_reload: whether we update the meter values or create them.
2654+
:txn: ovn northbound idl transaction.
2655+
2656+
"""
2657+
meter = self._nb_idl.db_find_rows(
2658+
"Meter", ("name", "=", meter_name)).execute(check_error=True)
2659+
# The meter is created when a log object is created, not by default.
2660+
# This condition avoids creating the meter if it wasn't there already
2661+
commands = []
2662+
if from_reload and not meter:
2663+
return
2664+
if meter:
2665+
meter = meter[0]
2666+
meter_band = self._nb_idl.lookup("Meter_Band",
2667+
meter.bands[0].uuid, default=None)
2668+
if meter_band:
2669+
if all((meter.unit == "pktps",
2670+
meter.fair[0],
2671+
meter_band.rate == cfg.CONF.network_log.rate_limit,
2672+
meter_band.burst_size ==
2673+
cfg.CONF.network_log.burst_limit)):
2674+
# Meter (and its meter-band) unchanged: noop.
2675+
return
2676+
# Re-create meter (and its meter-band) with the new attributes.
2677+
# This is supposed to happen only if configuration changed, so
2678+
# doing updates is an overkill: better to leverage the ovsdbapp
2679+
# library to avoid the complexity.
2680+
LOG.info("Deleting outdated log fair meter %s", meter_name)
2681+
commands.append(self._nb_idl.meter_del(meter.uuid))
2682+
# Create meter
2683+
LOG.info("Creating network log fair meter %s", meter_name)
2684+
commands.append(self._nb_idl.meter_add(
2685+
name=meter_name,
2686+
unit="pktps",
2687+
rate=cfg.CONF.network_log.rate_limit,
2688+
fair=True,
2689+
burst_size=cfg.CONF.network_log.burst_limit,
2690+
may_exist=False,
2691+
external_ids={ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY:
2692+
log_const.LOGGING_PLUGIN}))
2693+
self._transaction(commands, txn=txn)

neutron/services/logapi/drivers/ovn/driver.py

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -89,54 +89,14 @@ def _get_logs(self, context):
8989
log_objs = self._log_plugin.get_logs(context)
9090
return [self._log_dict_to_obj(lo) for lo in log_objs]
9191

92+
@property
93+
def _ovn_client(self):
94+
return self.plugin_driver._ovn_client
95+
9296
@property
9397
def ovn_nb(self):
9498
return self.plugin_driver.nb_ovn
9599

96-
def _create_ovn_fair_meter(self, ovn_txn):
97-
"""Create row in Meter table with fair attribute set to True.
98-
99-
Create a row in OVN's NB Meter table based on well-known name. This
100-
method uses the network_log configuration to specify the attributes
101-
of the meter. Current implementation needs only one 'fair' meter row
102-
which is then referred by multiple ACL rows.
103-
104-
:param ovn_txn: ovn northbound idl transaction.
105-
106-
"""
107-
meter = self.ovn_nb.db_find_rows(
108-
"Meter", ("name", "=", self.meter_name)).execute(check_error=True)
109-
if meter:
110-
meter = meter[0]
111-
try:
112-
meter_band = self.ovn_nb.lookup("Meter_Band",
113-
meter.bands[0].uuid)
114-
if all((meter.unit == "pktps",
115-
meter.fair[0],
116-
meter_band.rate == cfg.CONF.network_log.rate_limit,
117-
meter_band.burst_size ==
118-
cfg.CONF.network_log.burst_limit)):
119-
# Meter (and its meter-band) unchanged: noop.
120-
return
121-
except idlutils.RowNotFound:
122-
pass
123-
# Re-create meter (and its meter-band) with the new attributes.
124-
# This is supposed to happen only if configuration changed, so
125-
# doing updates is an overkill: better to leverage the ovsdbapp
126-
# library to avoid the complexity.
127-
ovn_txn.add(self.ovn_nb.meter_del(meter.uuid))
128-
# Create meter
129-
LOG.info("Creating network log fair meter %s", self.meter_name)
130-
ovn_txn.add(self.ovn_nb.meter_add(
131-
name=self.meter_name,
132-
unit="pktps",
133-
rate=cfg.CONF.network_log.rate_limit,
134-
fair=True,
135-
burst_size=cfg.CONF.network_log.burst_limit,
136-
may_exist=False,
137-
external_ids={ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY:
138-
log_const.LOGGING_PLUGIN}))
139-
140100
@staticmethod
141101
def _acl_actions_enabled(log_obj):
142102
if not log_obj.enabled:
@@ -303,7 +263,8 @@ def create_log(self, context, log_obj):
303263
pgs = self._pgs_from_log_obj(context, log_obj)
304264
actions_enabled = self._acl_actions_enabled(log_obj)
305265
with self.ovn_nb.transaction(check_error=True) as ovn_txn:
306-
self._create_ovn_fair_meter(ovn_txn)
266+
self._ovn_client.create_ovn_fair_meter(self.meter_name,
267+
txn=ovn_txn)
307268
self._set_acls_log(pgs, ovn_txn, actions_enabled,
308269
utils.ovn_name(log_obj.id))
309270

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,15 @@
3030
from neutron.db import ovn_revision_numbers_db as db_rev
3131
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import maintenance
3232
from neutron.tests.functional import base
33+
from neutron.tests.functional.services.logapi.drivers.ovn \
34+
import test_driver as test_log_driver
35+
3336
from neutron.tests.unit.api import test_extensions
3437
from neutron.tests.unit.extensions import test_extraroute
3538

39+
CFG_NEW_BURST = 50
40+
CFG_NEW_RATE = 150
41+
3642

3743
class _TestMaintenanceHelper(base.TestOVNFunctionalBase):
3844
"""A helper class to keep the code more organized."""
@@ -1042,3 +1048,37 @@ def _verify_lb(test, protocol, vip_ext_port, vip_int_port):
10421048

10431049
# Assert load balancer for port forwarding is gone
10441050
self.assertFalse(self._find_pf_lb(router_id, fip_id))
1051+
1052+
1053+
class TestLogMaintenance(_TestMaintenanceHelper,
1054+
test_log_driver.LogApiTestCaseBase):
1055+
def test_check_for_logging_conf_change(self):
1056+
# Check logging is supported
1057+
if not self.log_driver.network_logging_supported(self.nb_api):
1058+
self.skipTest("The current OVN version does not offer support "
1059+
"for neutron network log functionality.")
1060+
self.assertIsNotNone(self.log_plugin)
1061+
# Check no meter exists
1062+
self.assertFalse(self.nb_api._tables['Meter'].rows.values())
1063+
# Add a log object
1064+
self.log_plugin.create_log(self.context, self._log_data())
1065+
# Check a meter and fair meter exist
1066+
self.assertTrue(self.nb_api._tables['Meter'].rows)
1067+
self.assertTrue(self.nb_api._tables['Meter_Band'].rows)
1068+
self.assertEqual(cfg.CONF.network_log.burst_limit,
1069+
[*self.nb_api._tables['Meter_Band'].rows.values()][0].burst_size)
1070+
self.assertEqual(cfg.CONF.network_log.rate_limit,
1071+
[*self.nb_api._tables['Meter_Band'].rows.values()][0].rate)
1072+
# Update burst and rate limit values on the configuration
1073+
ovn_config.cfg.CONF.set_override('burst_limit', CFG_NEW_BURST,
1074+
group='network_log')
1075+
ovn_config.cfg.CONF.set_override('rate_limit', CFG_NEW_RATE,
1076+
group='network_log')
1077+
# Call the maintenance task
1078+
self.assertRaises(periodics.NeverAgain,
1079+
self.maint.check_fair_meter_consistency)
1080+
# Check meter band was effectively changed after the maintenance call
1081+
self.assertEqual(CFG_NEW_BURST,
1082+
[*self.nb_api._tables['Meter_Band'].rows.values()][0].burst_size)
1083+
self.assertEqual(CFG_NEW_RATE,
1084+
[*self.nb_api._tables['Meter_Band'].rows.values()][0].rate)

neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_client.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_client
2121
from neutron.tests import base
2222
from neutron.tests.unit import fake_resources as fakes
23+
from neutron.tests.unit.services.logapi.drivers.ovn \
24+
import test_driver as test_log_driver
2325
from neutron_lib.api.definitions import portbindings
26+
from neutron_lib.services.logapi import constants as log_const
2427

2528

2629
class TestOVNClientBase(base.BaseTestCase):
@@ -122,3 +125,61 @@ def test_vnic_remote_managed_port_context(self):
122125
self.assertEqual(
123126
self.fake_smartnic_hostname,
124127
self.ovn_client.determine_bind_host({}, port_context=context))
128+
129+
130+
class TestOVNClientFairMeter(TestOVNClientBase,
131+
test_log_driver.TestOVNDriverBase):
132+
133+
def test_create_ovn_fair_meter(self):
134+
mock_find_rows = mock.Mock()
135+
mock_find_rows.execute.return_value = None
136+
self.nb_idl.db_find_rows.return_value = mock_find_rows
137+
self.ovn_client.create_ovn_fair_meter(self._log_driver.meter_name)
138+
self.assertFalse(self.nb_idl.meter_del.called)
139+
self.assertTrue(self.nb_idl.meter_add.called)
140+
self.nb_idl.meter_add.assert_called_once_with(
141+
name=self._log_driver.meter_name,
142+
unit="pktps",
143+
rate=self.fake_cfg_network_log.rate_limit,
144+
fair=True,
145+
burst_size=self.fake_cfg_network_log.burst_limit,
146+
may_exist=False,
147+
external_ids={constants.OVN_DEVICE_OWNER_EXT_ID_KEY:
148+
log_const.LOGGING_PLUGIN})
149+
150+
def test_create_ovn_fair_meter_unchanged(self):
151+
mock_find_rows = mock.Mock()
152+
mock_find_rows.execute.return_value = [self._fake_meter()]
153+
self.nb_idl.db_find_rows.return_value = mock_find_rows
154+
self.nb_idl.lookup.side_effect = lambda table, key, default: (
155+
self._fake_meter_band() if key == "test_band" else default)
156+
self.ovn_client.create_ovn_fair_meter(self._log_driver.meter_name)
157+
self.assertFalse(self.nb_idl.meter_del.called)
158+
self.assertFalse(self.nb_idl.meter_add.called)
159+
160+
def test_create_ovn_fair_meter_changed(self):
161+
mock_find_rows = mock.Mock()
162+
mock_find_rows.execute.return_value = [self._fake_meter(fair=[False])]
163+
self.nb_idl.db_find_rows.return_value = mock_find_rows
164+
self.nb_idl.lookup.return_value = self._fake_meter_band()
165+
self.ovn_client.create_ovn_fair_meter(self._log_driver.meter_name)
166+
self.assertTrue(self.nb_idl.meter_del.called)
167+
self.assertTrue(self.nb_idl.meter_add.called)
168+
169+
def test_create_ovn_fair_meter_band_changed(self):
170+
mock_find_rows = mock.Mock()
171+
mock_find_rows.execute.return_value = [self._fake_meter()]
172+
self.nb_idl.db_find_rows.return_value = mock_find_rows
173+
self.nb_idl.lookup.return_value = self._fake_meter_band(rate=666)
174+
self.ovn_client.create_ovn_fair_meter(self._log_driver.meter_name)
175+
self.assertTrue(self.nb_idl.meter_del.called)
176+
self.assertTrue(self.nb_idl.meter_add.called)
177+
178+
def test_create_ovn_fair_meter_band_missing(self):
179+
mock_find_rows = mock.Mock()
180+
mock_find_rows.execute.return_value = [self._fake_meter()]
181+
self.nb_idl.db_find_rows.return_value = mock_find_rows
182+
self.nb_idl.lookup.side_effect = None
183+
self.ovn_client.create_ovn_fair_meter(self._log_driver.meter_name)
184+
self.assertTrue(self.nb_idl.meter_del.called)
185+
self.assertTrue(self.nb_idl.meter_add.called)

neutron/tests/unit/services/logapi/drivers/ovn/test_driver.py

Lines changed: 3 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
FAKE_CFG_BURST = 321
3030

3131

32-
class TestOVNDriver(base.BaseTestCase):
32+
class TestOVNDriverBase(base.BaseTestCase):
3333

3434
def setUp(self):
3535
super().setUp()
@@ -91,6 +91,8 @@ def _fake_meter_band(self, **kwargs):
9191
meter_band_obj_dict = {**meter_band_defaults_dict, **kwargs}
9292
return mock.Mock(**meter_band_obj_dict)
9393

94+
95+
class TestOVNDriver(TestOVNDriverBase):
9496
def test_create(self):
9597
driver = self._log_driver
9698
self.assertEqual(self.log_plugin, driver._log_plugin)
@@ -106,62 +108,6 @@ def test_create_meter_name(self):
106108
driver2 = self._log_driver_reinit()
107109
self.assertEqual(test_log_base, driver2.meter_name)
108110

109-
def test__create_ovn_fair_meter(self):
110-
mock_find_rows = mock.Mock()
111-
mock_find_rows.execute.return_value = None
112-
self._nb_ovn.db_find_rows.return_value = mock_find_rows
113-
self._log_driver._create_ovn_fair_meter(self._nb_ovn.transaction)
114-
self.assertFalse(self._nb_ovn.meter_del.called)
115-
self.assertTrue(self._nb_ovn.meter_add.called)
116-
self.assertFalse(
117-
self._nb_ovn.transaction.return_value.__enter__.called)
118-
self._nb_ovn.meter_add.assert_called_once_with(
119-
name="acl_log_meter",
120-
unit="pktps",
121-
rate=FAKE_CFG_RATE,
122-
fair=True,
123-
burst_size=FAKE_CFG_BURST,
124-
may_exist=False,
125-
external_ids={ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY:
126-
log_const.LOGGING_PLUGIN})
127-
128-
def test__create_ovn_fair_meter_unchanged(self):
129-
mock_find_rows = mock.Mock()
130-
mock_find_rows.execute.return_value = [self._fake_meter()]
131-
self._nb_ovn.db_find_rows.return_value = mock_find_rows
132-
self._nb_ovn.lookup.side_effect = lambda table, key: (
133-
self._fake_meter_band() if key == "test_band" else None)
134-
self._log_driver._create_ovn_fair_meter(self._nb_ovn.transaction)
135-
self.assertFalse(self._nb_ovn.meter_del.called)
136-
self.assertFalse(self._nb_ovn.meter_add.called)
137-
138-
def test__create_ovn_fair_meter_changed(self):
139-
mock_find_rows = mock.Mock()
140-
mock_find_rows.execute.return_value = [self._fake_meter(fair=[False])]
141-
self._nb_ovn.db_find_rows.return_value = mock_find_rows
142-
self._nb_ovn.lookup.return_value = self._fake_meter_band()
143-
self._log_driver._create_ovn_fair_meter(self._nb_ovn.transaction)
144-
self.assertTrue(self._nb_ovn.meter_del.called)
145-
self.assertTrue(self._nb_ovn.meter_add.called)
146-
147-
def test__create_ovn_fair_meter_band_changed(self):
148-
mock_find_rows = mock.Mock()
149-
mock_find_rows.execute.return_value = [self._fake_meter()]
150-
self._nb_ovn.db_find_rows.return_value = mock_find_rows
151-
self._nb_ovn.lookup.return_value = self._fake_meter_band(rate=666)
152-
self._log_driver._create_ovn_fair_meter(self._nb_ovn.transaction)
153-
self.assertTrue(self._nb_ovn.meter_del.called)
154-
self.assertTrue(self._nb_ovn.meter_add.called)
155-
156-
def test__create_ovn_fair_meter_band_missing(self):
157-
mock_find_rows = mock.Mock()
158-
mock_find_rows.execute.return_value = [self._fake_meter()]
159-
self._nb_ovn.db_find_rows.return_value = mock_find_rows
160-
self._nb_ovn.lookup.side_effect = idlutils.RowNotFound
161-
self._log_driver._create_ovn_fair_meter(self._nb_ovn.transaction)
162-
self.assertTrue(self._nb_ovn.meter_del.called)
163-
self.assertTrue(self._nb_ovn.meter_add.called)
164-
165111
class _fake_acl():
166112
def __init__(self, name=None, **acl_dict):
167113
acl_defaults_dict = {

0 commit comments

Comments
 (0)