Skip to content

Commit 36e0414

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Consider logging options when using OVNdbsync" into stable/2025.1
2 parents d9269bc + fa93909 commit 36e0414

File tree

9 files changed

+295
-32
lines changed

9 files changed

+295
-32
lines changed

neutron/common/ovn/acl.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ def add_acls_for_drop_port_group(pg_name):
148148
"name": [],
149149
"severity": [],
150150
"direction": direction,
151-
"match": '{} == @{} && ip'.format(p, pg_name)}
151+
"match": '{} == @{} && ip'.format(p, pg_name),
152+
"meter": []}
152153
acl_list.append(acl)
153154
return acl_list
154155

@@ -167,6 +168,7 @@ def drop_all_ip_traffic_for_port(port):
167168
"severity": [],
168169
"direction": direction,
169170
"match": '{} == "{}" && ip'.format(p, port['id']),
171+
"meter": [],
170172
"external_ids": {'neutron:lport': port['id']}}
171173
acl_list.append(acl)
172174
return acl_list
@@ -187,6 +189,7 @@ def add_sg_rule_acl_for_port_group(port_group, r, stateful, match):
187189
"severity": [],
188190
"direction": dir_map[r['direction']],
189191
"match": match,
192+
"meter": [],
190193
ovn_const.OVN_SG_RULE_EXT_ID_KEY: r['id']}
191194
return acl
192195

neutron/common/ovn/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@
259259

260260
ACL_EXPECTED_COLUMNS_NBDB = (
261261
'external_ids', 'direction', 'log', 'priority',
262-
'name', 'action', 'severity', 'match')
262+
'name', 'action', 'severity', 'match', 'meter')
263263

264264
# Resource types
265265
TYPE_NETWORKS = 'networks'

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

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.extensions import qos \
3636
as ovn_qos
3737
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_client
38+
from neutron.services.logapi.drivers.ovn import driver as log_driver
3839
from neutron.services.segments import db as segments_db
3940

4041

@@ -82,6 +83,18 @@ def __init__(self, core_plugin, ovn_api, sb_ovn, mode, ovn_driver):
8283
self.segments_plugin = (
8384
manager.NeutronManager.load_class_for_provider(
8485
'neutron.service_plugins', 'segments')())
86+
self.log_plugin = directory.get_plugin(plugin_constants.LOG_API)
87+
if not self.log_plugin:
88+
self.log_plugin = (
89+
manager.NeutronManager.load_class_for_provider(
90+
'neutron.service_plugins', 'log')())
91+
directory.add_plugin(plugin_constants.LOG_API, self.log_plugin)
92+
for driver in self.log_plugin.driver_manager.drivers:
93+
if driver.name == "ovn":
94+
self.ovn_log_driver = driver
95+
if not hasattr(self, 'ovn_log_driver'):
96+
self.ovn_log_driver = log_driver.OVNDriver()
97+
self.log_plugin.driver_manager.register_driver(self.ovn_log_driver)
8598

8699
def stop(self):
87100
if utils.is_ovn_l3(self.l3_plugin):
@@ -233,6 +246,9 @@ def sync_port_groups(self, ctx):
233246

234247
def _get_acls_from_port_groups(self):
235248
ovn_acls = []
249+
# Options and label columns are only present for OVN >= 22.03.
250+
# Furthermore label is a randint so it cannot be compared with any
251+
# expected neutron value. They are added later on the ACL addition.
236252
acl_columns = (self.ovn_api._tables['ACL'].columns.keys() &
237253
set(ovn_const.ACL_EXPECTED_COLUMNS_NBDB))
238254
acl_columns.discard('external_ids')
@@ -244,7 +260,16 @@ def _get_acls_from_port_groups(self):
244260
acl_string['port_group'] = pg.name
245261
if id_key in acl.external_ids:
246262
acl_string[id_key] = acl.external_ids[id_key]
263+
# This properties are present as lists of one item,
264+
# converting them to string.
265+
if acl_string['name']:
266+
acl_string['name'] = acl_string['name'][0]
267+
if acl_string['meter']:
268+
acl_string['meter'] = acl_string['meter'][0]
269+
if acl_string['severity']:
270+
acl_string['severity'] = acl_string['severity'][0]
247271
ovn_acls.append(acl_string)
272+
248273
return ovn_acls
249274

250275
def sync_acls(self, ctx):
@@ -274,6 +299,10 @@ def sync_acls(self, ctx):
274299
neutron_default_acls = acl_utils.add_acls_for_drop_port_group(
275300
ovn_const.OVN_DROP_PORT_GROUP_NAME)
276301

302+
# Add logging options
303+
self.ovn_log_driver.add_logging_options_to_acls(neutron_acls, ctx)
304+
self.ovn_log_driver.add_logging_options_to_acls(neutron_default_acls,
305+
ctx)
277306
ovn_acls = self._get_acls_from_port_groups()
278307
# Sort the acls in the ovn database according to the security
279308
# group rule id for easy comparison in the future.
@@ -304,14 +333,24 @@ def sync_acls(self, ctx):
304333
o_index += 1
305334
elif n_id == o_id:
306335
if any(item not in na.items() for item in oa.items()):
336+
for item in oa.items():
337+
if item not in na.items():
338+
LOG.warning('Property %(item)s from OVN ACL not '
339+
'found in Neutron ACL: %(n_acl)s',
340+
{'item': item,
341+
'n_acl': na})
307342
add_acls.append(na)
308343
remove_acls.append(oa)
309344
n_index += 1
310345
o_index += 1
311346
elif n_id > o_id:
347+
LOG.warning('ACL should not be present in OVN, removing'
348+
'%(acl)s', {'acl': oa})
312349
remove_acls.append(oa)
313350
o_index += 1
314351
else:
352+
LOG.warning('ACL should be present in OVN but is not, adding:'
353+
'%(acl)s', {'acl': na})
315354
add_acls.append(na)
316355
n_index += 1
317356

@@ -351,15 +390,41 @@ def get_num_acls(ovn_acls):
351390
if (self.mode == ovn_const.OVN_DB_SYNC_MODE_REPAIR and
352391
(num_acls_to_add or num_acls_to_remove)):
353392
one_time_pg_resync = True
393+
with self.ovn_api.transaction(check_error=True) as txn:
394+
for aclr in ovn_acls:
395+
LOG.warning('ACLs found in OVN NB DB but not in '
396+
'Neutron for port group %s',
397+
aclr['port_group'])
398+
txn.add(self.ovn_api.pg_acl_del(aclr['port_group'],
399+
aclr['direction'],
400+
aclr['priority'],
401+
aclr['match']))
402+
for aclr in ovn_acls_from_ls:
403+
# Remove all the ACLs from any Logical Switch if they have
404+
# any. Elements are (lswitch_name, list_of_acls).
405+
if len(aclr[1]) > 0:
406+
LOG.warning('Removing ACLs from OVN from Logical '
407+
'Switch %s', aclr[0])
408+
txn.add(self.ovn_api.acl_del(aclr[0]))
354409
while True:
355410
try:
356411
with self.ovn_api.transaction(check_error=True) as txn:
357412
for acla in neutron_acls:
358413
LOG.warning('ACL found in Neutron but not in '
359414
'OVN NB DB for port group %s',
360415
acla['port_group'])
361-
txn.add(self.ovn_api.pg_acl_add(
362-
**acla, may_exist=True))
416+
acl = txn.add(self.ovn_api.pg_acl_add(**acla,
417+
may_exist=True))
418+
# We need to do this now since label should be
419+
# random and not 0. We can use options as a way
420+
# to see if label is supported or not.
421+
if acla.get('log'):
422+
self.ovn_log_driver.add_label_related(acla,
423+
ctx)
424+
txn.add(self.ovn_api.db_set('ACL', acl,
425+
label=acla['label'],
426+
options=acla['options']))
427+
363428
except idlutils.RowNotFound as row_err:
364429
if row_err.msg.startswith("Cannot find Port_Group"):
365430
if one_time_pg_resync:
@@ -377,23 +442,6 @@ def get_num_acls(ovn_acls):
377442
raise
378443
break
379444

380-
with self.ovn_api.transaction(check_error=True) as txn:
381-
for aclr in ovn_acls:
382-
LOG.warning('ACLs found in OVN NB DB but not in '
383-
'Neutron for port group %s',
384-
aclr['port_group'])
385-
txn.add(self.ovn_api.pg_acl_del(aclr['port_group'],
386-
aclr['direction'],
387-
aclr['priority'],
388-
aclr['match']))
389-
for aclr in ovn_acls_from_ls:
390-
# Remove all the ACLs from any Logical Switch if they have
391-
# any. Elements are (lswitch_name, list_of_acls).
392-
if len(aclr[1]) > 0:
393-
LOG.warning('Removing ACLs from OVN from Logical '
394-
'Switch %s', aclr[0])
395-
txn.add(self.ovn_api.acl_del(aclr[0]))
396-
397445
LOG.debug('OVN-NB Sync ACLs completed @ %s', str(datetime.now()))
398446

399447
def _calculate_routes_differences(self, ovn_routes, db_routes):

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

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,80 @@ def resource_update(self, context, log_objs):
409409
with self.ovn_nb.transaction(check_error=True) as ovn_txn:
410410
self._update_log_objs(context, ovn_txn, log_objs)
411411

412+
def add_logging_options_to_acls(self, neutron_acls, context):
413+
log_objs = self._get_logs(context)
414+
for log_obj in log_objs:
415+
pgs = self._pgs_from_log_obj(context, log_obj)
416+
actions_enabled = self._acl_actions_enabled(log_obj)
417+
self._set_neutron_acls_log(pgs, context, actions_enabled,
418+
utils.ovn_name(log_obj.id),
419+
neutron_acls)
420+
421+
# This function is a version of set_acls_log meant to change neutron
422+
# defined acls, mostly thought for ovndbsync consistency check.
423+
def _set_neutron_acls_log(self, pgs, context, actions_enabled, log_name,
424+
neutron_acls):
425+
acl_changes, acl_visits = 0, 0
426+
for pg in pgs:
427+
meter_name = self.meter_name
428+
if pg['name'] != ovn_const.OVN_DROP_PORT_GROUP_NAME:
429+
sg = sg_obj.SecurityGroup.get_sg_by_id(context,
430+
pg['external_ids'][ovn_const.OVN_SG_EXT_ID_KEY])
431+
if not sg:
432+
LOG.warning("Port Group %s is missing a corresponding "
433+
"security group, skipping its network log "
434+
"setting...", pg["name"])
435+
continue
436+
if not sg.stateful:
437+
meter_name = meter_name + ("_stateless")
438+
# We need to get the OVN ACL because UUID is not listed as a
439+
# property on neutron defined ACLs (and it shouldn't), so we need
440+
# to check which ACL is that UUID referring to, using match as
441+
# differentiating value.
442+
for acl in neutron_acls:
443+
acl_visits += 1
444+
# skip acls used by a different network log
445+
n_acl_name = acl['name']
446+
if n_acl_name and n_acl_name != log_name:
447+
continue
448+
action = acl['action'] in actions_enabled
449+
acl['log'] = action
450+
acl['meter'] = meter_name
451+
acl['name'] = log_name
452+
acl['severity'] = "info"
453+
if acl.get('options'):
454+
acl["options"] = {'log-related': "true"}
455+
# label is not set because the actual number should not
456+
# be compared or taken into account, we only need it to be
457+
# different from 0.
458+
acl_changes += 1
459+
LOG.info("Set %d (out of %d visited) Neutron ACLs for network log %s",
460+
acl_changes, acl_visits, log_name)
461+
462+
def _get_all_log_pgs(self, ctx):
463+
"""Get all Port Group names associated to a Log Object.
464+
465+
:param log_plugin: Currently loaded log_plugging.
466+
:param ctx: current running context information
467+
"""
468+
log_objs = self._get_logs(ctx)
469+
log_pgs = []
470+
for log_obj in log_objs:
471+
log_pgs.extend(self._pgs_from_log_obj(ctx, log_obj))
472+
return log_pgs
473+
474+
def add_label_related(self, n_acl, ctx):
475+
# Get acls to be able to check if label is present in OVN ACLs and
476+
# also check old label value for ACL if it was already present.
477+
acls = [acl for pg in self._get_all_log_pgs(ctx) for acl in pg["acls"]]
478+
if not acls:
479+
return
480+
acl = self.ovn_nb.lookup("ACL", acls[0], default=None)
481+
if not hasattr(acl, 'label'):
482+
return
483+
n_acl["label"] = secrets.SystemRandom().randrange(1, MAX_INT_LABEL)
484+
n_acl["options"] = {'log-related': 'true'}
485+
412486

413487
def register(plugin_driver):
414488
"""Register the driver."""

neutron/tests/functional/base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import worker
5151
from neutron.plugins.ml2.drivers import type_geneve # noqa
5252
from neutron import service # noqa
53+
from neutron.services.logapi.drivers.ovn import driver as log_driver
5354
from neutron.tests import base
5455
from neutron.tests.common import base as common_base
5556
from neutron.tests.common import helpers
@@ -175,6 +176,7 @@ def setUp(self, maintenance_worker=False, service_plugins=None):
175176
self.addCleanup(exts.PluginAwareExtensionManager.clear_instance)
176177
self.ovsdb_server_mgr = None
177178
self._service_plugins = service_plugins
179+
log_driver.DRIVER = None
178180
super().setUp()
179181
self.test_log_dir = os.path.join(DEFAULT_LOG_DIR, self.id())
180182
base.setup_test_logging(
@@ -197,6 +199,11 @@ def setUp(self, maintenance_worker=False, service_plugins=None):
197199
self.mech_driver.log_driver)
198200
self.mech_driver.log_driver.plugin_driver = self.mech_driver
199201
self.mech_driver.log_driver._log_plugin_property = None
202+
for driver in self.log_plugin.driver_manager.drivers:
203+
if driver.name == "ovn":
204+
self.ovn_log_driver = driver
205+
if not hasattr(self, 'ovn_log_driver'):
206+
self.ovn_log_driver = log_driver.OVNDriver()
200207
self.ovn_northd_mgr = None
201208
self.maintenance_worker = maintenance_worker
202209
mock.patch(

0 commit comments

Comments
 (0)