Skip to content

Commit 34e7468

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "[OVN] Fix rate and burst for stateless security groups" into stable/2023.1
2 parents cc209f4 + 226220a commit 34e7468

File tree

6 files changed

+102
-26
lines changed

6 files changed

+102
-26
lines changed

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

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2685,7 +2685,8 @@ def add_txns_to_remove_port_dns_records(self, txn, port):
26852685
txn.add(self._nb_idl.dns_remove_record(
26862686
ls_dns_record.uuid, ptr_record, if_exists=True))
26872687

2688-
def create_ovn_fair_meter(self, meter_name, from_reload=False, txn=None):
2688+
def _create_ovn_fair_meter(self, meter_name, from_reload=False, txn=None,
2689+
stateless=False):
26892690
"""Create row in Meter table with fair attribute set to True.
26902691
26912692
Create a row in OVN's NB Meter table based on well-known name. This
@@ -2700,21 +2701,35 @@ def create_ovn_fair_meter(self, meter_name, from_reload=False, txn=None):
27002701
"""
27012702
meter = self._nb_idl.db_find_rows(
27022703
"Meter", ("name", "=", meter_name)).execute(check_error=True)
2703-
# The meter is created when a log object is created, not by default.
2704-
# This condition avoids creating the meter if it wasn't there already
2704+
# The meters are created when a log object is created, not by default.
2705+
# This condition avoids creating the meter if it wasn't there already.
27052706
commands = []
27062707
if from_reload and not meter:
27072708
return
2709+
2710+
burst_limit = cfg.CONF.network_log.burst_limit
2711+
rate_limit = cfg.CONF.network_log.rate_limit
2712+
if stateless:
2713+
meter_name = meter_name + "_stateless"
2714+
burst_limit = int(burst_limit / 2)
2715+
rate_limit = int(rate_limit / 2)
2716+
# The stateless meter is only created once the stateful meter was
2717+
# successfully created.
2718+
# The treatment of limits is not equal for stateful and stateless
2719+
# traffic at a kernel level according to:
2720+
# https://bugzilla.redhat.com/show_bug.cgi?id=2212952
2721+
# The stateless meter is created to adjust this issue.
2722+
meter = self._nb_idl.db_find_rows(
2723+
"Meter", ("name", "=", meter_name)).execute(check_error=True)
27082724
if meter:
27092725
meter = meter[0]
27102726
meter_band = self._nb_idl.lookup("Meter_Band",
27112727
meter.bands[0].uuid, default=None)
27122728
if meter_band:
27132729
if all((meter.unit == "pktps",
27142730
meter.fair[0],
2715-
meter_band.rate == cfg.CONF.network_log.rate_limit,
2716-
meter_band.burst_size ==
2717-
cfg.CONF.network_log.burst_limit)):
2731+
meter_band.rate == rate_limit,
2732+
meter_band.burst_size == burst_limit)):
27182733
# Meter (and its meter-band) unchanged: noop.
27192734
return
27202735
# Re-create meter (and its meter-band) with the new attributes.
@@ -2728,10 +2743,15 @@ def create_ovn_fair_meter(self, meter_name, from_reload=False, txn=None):
27282743
commands.append(self._nb_idl.meter_add(
27292744
name=meter_name,
27302745
unit="pktps",
2731-
rate=cfg.CONF.network_log.rate_limit,
2746+
rate=rate_limit,
27322747
fair=True,
2733-
burst_size=cfg.CONF.network_log.burst_limit,
2748+
burst_size=burst_limit,
27342749
may_exist=False,
27352750
external_ids={ovn_const.OVN_DEVICE_OWNER_EXT_ID_KEY:
27362751
log_const.LOGGING_PLUGIN}))
27372752
self._transaction(commands, txn=txn)
2753+
2754+
def create_ovn_fair_meter(self, meter_name, from_reload=False, txn=None):
2755+
self._create_ovn_fair_meter(meter_name, from_reload, txn)
2756+
self._create_ovn_fair_meter(meter_name, from_reload, txn,
2757+
stateless=True)

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from neutron.common.ovn import constants as ovn_const
2929
from neutron.common.ovn import utils
3030
from neutron.conf.services import logging as log_cfg
31+
from neutron.objects import securitygroup as sg_obj
3132
from neutron.services.logapi.common import db_api
3233
from neutron.services.logapi.common import sg_callback
3334
from neutron.services.logapi.drivers import base
@@ -152,9 +153,17 @@ def _remove_acls_log(self, pgs, ovn_txn, log_name=None):
152153
msg += " for network log {}".format(log_name)
153154
LOG.info(msg, acl_changes, acl_absents, acl_visits)
154155

155-
def _set_acls_log(self, pgs, ovn_txn, actions_enabled, log_name):
156+
def _set_acls_log(self, pgs, context, ovn_txn, actions_enabled, log_name):
156157
acl_changes, acl_visits = 0, 0
157158
for pg in pgs:
159+
meter_name = self.meter_name
160+
if ovn_const.OVN_DROP_PORT_GROUP_NAME not in pg["name"]:
161+
stateful = (sg_obj.SecurityGroup
162+
.get_sg_by_id(context, pg["name"]
163+
.replace('pg_', '', 1)
164+
.replace('_', '-')).stateful)
165+
if not stateful:
166+
meter_name = meter_name + ("_stateless")
158167
for acl_uuid in pg["acls"]:
159168
acl_visits += 1
160169
acl = self.ovn_nb.lookup("ACL", acl_uuid)
@@ -163,7 +172,7 @@ def _set_acls_log(self, pgs, ovn_txn, actions_enabled, log_name):
163172
continue
164173
columns = {
165174
'log': acl.action in actions_enabled,
166-
'meter': self.meter_name,
175+
'meter': meter_name,
167176
'name': log_name,
168177
'severity': "info"
169178
}
@@ -183,7 +192,7 @@ def _update_log_objs(self, context, ovn_txn, log_objs):
183192
for log_obj in log_objs:
184193
pgs = self._pgs_from_log_obj(context, log_obj)
185194
actions_enabled = self._acl_actions_enabled(log_obj)
186-
self._set_acls_log(pgs, ovn_txn, actions_enabled,
195+
self._set_acls_log(pgs, context, ovn_txn, actions_enabled,
187196
utils.ovn_name(log_obj.id))
188197

189198
def _pgs_all(self):
@@ -266,7 +275,7 @@ def create_log(self, context, log_obj):
266275
with self.ovn_nb.transaction(check_error=True) as ovn_txn:
267276
self._ovn_client.create_ovn_fair_meter(self.meter_name,
268277
txn=ovn_txn)
269-
self._set_acls_log(pgs, ovn_txn, actions_enabled,
278+
self._set_acls_log(pgs, context, ovn_txn, actions_enabled,
270279
utils.ovn_name(log_obj.id))
271280

272281
def create_log_precommit(self, context, log_obj):
@@ -334,7 +343,7 @@ def update_log(self, context, log_obj):
334343
if not self._unset_disabled_acls(context, log_obj, ovn_txn):
335344
pgs = self._pgs_from_log_obj(context, log_obj)
336345
actions_enabled = self._acl_actions_enabled(log_obj)
337-
self._set_acls_log(pgs, ovn_txn, actions_enabled,
346+
self._set_acls_log(pgs, context, ovn_txn, actions_enabled,
338347
utils.ovn_name(log_obj.id))
339348

340349
def delete_log(self, context, log_obj):
@@ -356,6 +365,8 @@ def delete_log(self, context, log_obj):
356365
self._remove_acls_log(pgs, ovn_txn)
357366
ovn_txn.add(self.ovn_nb.meter_del(self.meter_name,
358367
if_exists=True))
368+
ovn_txn.add(self.ovn_nb.meter_del(
369+
self.meter_name + "_stateless", if_exists=True))
359370
LOG.info("All ACL logs cleared after deletion of log_obj %s",
360371
log_obj.id)
361372
return

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,10 +1026,9 @@ def test_check_for_logging_conf_change(self):
10261026
# Check a meter and fair meter exist
10271027
self.assertTrue(self.nb_api._tables['Meter'].rows)
10281028
self.assertTrue(self.nb_api._tables['Meter_Band'].rows)
1029-
self.assertEqual(cfg.CONF.network_log.burst_limit,
1030-
[*self.nb_api._tables['Meter_Band'].rows.values()][0].burst_size)
1031-
self.assertEqual(cfg.CONF.network_log.rate_limit,
1032-
[*self.nb_api._tables['Meter_Band'].rows.values()][0].rate)
1029+
self.assertEqual(len([*self.nb_api._tables['Meter'].rows.values()]),
1030+
len([*self.nb_api._tables['Meter_Band'].rows.values()]))
1031+
self._check_meters_consistency()
10331032
# Update burst and rate limit values on the configuration
10341033
ovn_config.cfg.CONF.set_override('burst_limit', CFG_NEW_BURST,
10351034
group='network_log')
@@ -1039,7 +1038,16 @@ def test_check_for_logging_conf_change(self):
10391038
self.assertRaises(periodics.NeverAgain,
10401039
self.maint.check_fair_meter_consistency)
10411040
# Check meter band was effectively changed after the maintenance call
1042-
self.assertEqual(CFG_NEW_BURST,
1043-
[*self.nb_api._tables['Meter_Band'].rows.values()][0].burst_size)
1044-
self.assertEqual(CFG_NEW_RATE,
1045-
[*self.nb_api._tables['Meter_Band'].rows.values()][0].rate)
1041+
self._check_meters_consistency(CFG_NEW_BURST, CFG_NEW_RATE)
1042+
1043+
def _check_meters_consistency(self, new_burst=None, new_rate=None):
1044+
burst, rate = (new_burst, new_rate) if new_burst else (
1045+
cfg.CONF.network_log.burst_limit, cfg.CONF.network_log.rate_limit)
1046+
for meter in [*self.nb_api._tables['Meter'].rows.values()]:
1047+
meter_band = self.nb_api.lookup('Meter_Band', meter.bands[0].uuid)
1048+
if "_stateless" in meter.name:
1049+
self.assertEqual(int(burst / 2), meter_band.burst_size)
1050+
self.assertEqual(int(rate / 2), meter_band.rate)
1051+
else:
1052+
self.assertEqual(burst, meter_band.burst_size)
1053+
self.assertEqual(rate, meter_band.rate)

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ def setUp(self):
3030
self._check_is_supported()
3131
self.ctxt = context.Context('admin', 'fake_tenant')
3232

33+
# Since these tests use the _create_network() from the unit test suite
34+
# but _create_security_group() is from the functional tests, two
35+
# different tenant_ids will be used unless we specify the following
36+
# line in the code:
37+
self._tenant_id = self.ctxt.project_id
38+
3339
def _check_is_supported(self):
3440
if not self.log_driver.network_logging_supported(self.nb_api):
3541
self.skipTest("The current OVN version does not offer support "

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

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,16 @@ def test_create_ovn_fair_meter(self):
222222
self.ovn_client.create_ovn_fair_meter(self._log_driver.meter_name)
223223
self.assertFalse(self.nb_idl.meter_del.called)
224224
self.assertTrue(self.nb_idl.meter_add.called)
225-
self.nb_idl.meter_add.assert_called_once_with(
225+
self.nb_idl.meter_add.assert_any_call(
226+
name=self._log_driver.meter_name + "_stateless",
227+
unit="pktps",
228+
rate=int(self.fake_cfg_network_log.rate_limit / 2),
229+
fair=True,
230+
burst_size=int(self.fake_cfg_network_log.burst_limit / 2),
231+
may_exist=False,
232+
external_ids={constants.OVN_DEVICE_OWNER_EXT_ID_KEY:
233+
log_const.LOGGING_PLUGIN})
234+
self.nb_idl.meter_add.assert_any_call(
226235
name=self._log_driver.meter_name,
227236
unit="pktps",
228237
rate=self.fake_cfg_network_log.rate_limit,
@@ -234,10 +243,17 @@ def test_create_ovn_fair_meter(self):
234243

235244
def test_create_ovn_fair_meter_unchanged(self):
236245
mock_find_rows = mock.Mock()
237-
mock_find_rows.execute.return_value = [self._fake_meter()]
246+
fake_meter1 = [self._fake_meter()]
247+
fake_meter2 = [self._fake_meter(
248+
name=self._log_driver.meter_name + "_stateless",
249+
bands=[mock.Mock(uuid='tb_stateless')])]
250+
mock_find_rows.execute.side_effect = [fake_meter1, fake_meter1,
251+
fake_meter2, fake_meter2]
238252
self.nb_idl.db_find_rows.return_value = mock_find_rows
239253
self.nb_idl.lookup.side_effect = lambda table, key, default: (
240-
self._fake_meter_band() if key == "test_band" else default)
254+
self._fake_meter_band() if key == "test_band" else
255+
self._fake_meter_band_stateless() if key == "tb_stateless" else
256+
default)
241257
self.ovn_client.create_ovn_fair_meter(self._log_driver.meter_name)
242258
self.assertFalse(self.nb_idl.meter_del.called)
243259
self.assertFalse(self.nb_idl.meter_add.called)

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
from neutron.common.ovn import constants as ovn_const
2323
from neutron.common.ovn import utils as ovn_utils
24+
from neutron.objects import securitygroup as sg_obj
2425
from neutron.services.logapi.drivers.ovn import driver as ovn_driver
2526
from neutron.tests import base
2627
from neutron.tests.unit import fake_resources
@@ -92,6 +93,15 @@ def _fake_meter_band(self, **kwargs):
9293
meter_band_obj_dict = {**meter_band_defaults_dict, **kwargs}
9394
return mock.Mock(**meter_band_obj_dict)
9495

96+
def _fake_meter_band_stateless(self, **kwargs):
97+
meter_band_defaults_dict = {
98+
'uuid': 'tb_stateless',
99+
'rate': int(self.fake_cfg_network_log.rate_limit / 2),
100+
'burst_size': int(self.fake_cfg_network_log.burst_limit / 2),
101+
}
102+
meter_band_obj_dict = {**meter_band_defaults_dict, **kwargs}
103+
return mock.Mock(**meter_band_obj_dict)
104+
95105

96106
class TestOVNDriver(TestOVNDriverBase):
97107
def test_create(self):
@@ -287,7 +297,8 @@ def _mock_lookup(_pg_table, acl_uuid, default):
287297
self._nb_ovn.db_set.call_count)
288298

289299
@mock.patch.object(ovn_driver.LOG, 'info')
290-
def test__set_acls_log(self, m_info):
300+
@mock.patch.object(sg_obj.SecurityGroup, 'get_sg_by_id')
301+
def test__set_acls_log(self, get_sg, m_info):
291302
pg_dict = self._fake_pg_dict(acls=['acl1', 'acl2', 'acl3', 'acl4'])
292303
log_name = 'test_obj_name'
293304
used_name = 'test_used_name'
@@ -297,10 +308,14 @@ def _mock_lookup(_pg_table, acl_uuid):
297308
return self._fake_acl()
298309
return self._fake_acl(name=used_name)
299310

311+
sg = fake_resources.FakeSecurityGroup.create_one_security_group(
312+
attrs={'stateful': True})
313+
get_sg.return_value = sg
300314
self._nb_ovn.lookup.side_effect = _mock_lookup
301315
actions_enabled = self._log_driver._acl_actions_enabled(
302316
self._fake_log_obj(event=log_const.ALL_EVENT))
303-
self._log_driver._set_acls_log([pg_dict], self._nb_ovn.transaction,
317+
self._log_driver._set_acls_log([pg_dict], self.context,
318+
self._nb_ovn.transaction,
304319
actions_enabled, log_name)
305320
info_args, _info_kwargs = m_info.call_args_list[0]
306321
self.assertIn('Set %d (out of %d visited) ACLs for network log %s',

0 commit comments

Comments
 (0)