Skip to content

Commit 61f01cd

Browse files
committed
[OVN] Handle missing acls during log removal
During log delete we fetch all the acls for all the pgs if there is only one log object and do clear log of all these acls, but if one or more acls of any of the pgs is removed concurrently, db_set fails as acl is not found. This patch proposes to only do log clear of those acls which are available and add log message for the acls which were deleted concurrently. Also add a unit test for this case where one of the acl get's missing. Closes-Bug: #1971569 Change-Id: I58487024c8d0352776307f0185f0812bb00036ae (cherry picked from commit 1471f53)
1 parent f181a8f commit 61f01cd

File tree

2 files changed

+40
-10
lines changed

2 files changed

+40
-10
lines changed

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,19 @@ def _acl_actions_enabled(log_obj):
154154
ovn_const.ACL_ACTION_ALLOW}
155155

156156
def _remove_acls_log(self, pgs, ovn_txn, log_name=None):
157-
acl_changes, acl_visits = 0, 0
157+
acl_absents, acl_changes, acl_visits = 0, 0, 0
158158
for pg in pgs:
159159
for acl_uuid in pg["acls"]:
160160
acl_visits += 1
161+
acl = self.ovn_nb.lookup("ACL", acl_uuid, default=None)
162+
# Log message if ACL is not found, as deleted concurrently
163+
if acl is None:
164+
LOG.debug("ACL %s not found, deleted concurrently",
165+
acl_uuid)
166+
acl_absents += 1
167+
continue
161168
# skip acls used by a different network log
162169
if log_name:
163-
acl = self.ovn_nb.lookup("ACL", acl_uuid)
164170
if acl.name and acl.name[0] != log_name:
165171
continue
166172
ovn_txn.add(self.ovn_nb.db_set(
@@ -171,10 +177,10 @@ def _remove_acls_log(self, pgs, ovn_txn, log_name=None):
171177
("severity", [])
172178
))
173179
acl_changes += 1
174-
msg = "Cleared %d (out of %d visited) ACLs"
180+
msg = "Cleared %d, Not found %d (out of %d visited) ACLs"
175181
if log_name:
176182
msg += " for network log {}".format(log_name)
177-
LOG.info(msg, acl_changes, acl_visits)
183+
LOG.info(msg, acl_changes, acl_absents, acl_visits)
178184

179185
def _set_acls_log(self, pgs, ovn_txn, actions_enabled, log_name):
180186
acl_changes, acl_visits = 0, 0

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

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -270,19 +270,41 @@ def test__remove_acls_log(self, m_info):
270270
pg_dict = self._fake_pg_dict(acls=['acl1', 'acl2'])
271271
self._log_driver._remove_acls_log([pg_dict], self._nb_ovn.transaction)
272272
info_args, _info_kwargs = m_info.call_args_list[0]
273-
self.assertIn('Cleared %d (out of %d visited) ACLs', info_args[0])
274-
self._nb_ovn.lookup.assert_not_called()
273+
self.assertIn('Cleared %d, Not found %d (out of %d visited) ACLs',
274+
info_args[0])
275+
self._nb_ovn.lookup.assert_has_calls([
276+
mock.call('ACL', 'acl1', default=None),
277+
mock.call('ACL', 'acl2', default=None)])
275278
self.assertEqual(len(pg_dict["acls"]), info_args[1])
276-
self.assertEqual(len(pg_dict["acls"]), info_args[2])
279+
self.assertEqual(len(pg_dict["acls"]) - 2, info_args[2])
280+
self.assertEqual(len(pg_dict["acls"]), info_args[3])
277281
self.assertEqual(len(pg_dict["acls"]), self._nb_ovn.db_set.call_count)
278282

283+
@mock.patch.object(ovn_driver.LOG, 'info')
284+
def test__remove_acls_log_missing_acls(self, m_info):
285+
pg_dict = self._fake_pg_dict(acls=['acl1', 'acl2', 'acl3'])
286+
287+
def _mock_lookup(_pg_table, acl_uuid, default):
288+
if acl_uuid == 'acl3':
289+
return None
290+
return self._fake_acl()
291+
292+
self._nb_ovn.lookup.side_effect = _mock_lookup
293+
self._log_driver._remove_acls_log([pg_dict], self._nb_ovn.transaction)
294+
info_args, _info_kwargs = m_info.call_args_list[0]
295+
self.assertEqual(len(pg_dict["acls"]) - 1, info_args[1])
296+
self.assertEqual(len(pg_dict["acls"]) - 2, info_args[2])
297+
self.assertEqual(len(pg_dict["acls"]), info_args[3])
298+
self.assertEqual(len(pg_dict["acls"]) - 1,
299+
self._nb_ovn.db_set.call_count)
300+
279301
@mock.patch.object(ovn_driver.LOG, 'info')
280302
def test__remove_acls_log_with_log_name(self, m_info):
281303
pg_dict = self._fake_pg_dict(acls=['acl1', 'acl2', 'acl3', 'acl4'])
282304
log_name = 'test_obj_name'
283305
used_name = 'test_used_name'
284306

285-
def _mock_lookup(_pg_table, acl_uuid):
307+
def _mock_lookup(_pg_table, acl_uuid, default):
286308
if acl_uuid == 'acl2':
287309
return self._fake_acl(name=used_name)
288310
return self._fake_acl(name=log_name)
@@ -291,10 +313,12 @@ def _mock_lookup(_pg_table, acl_uuid):
291313
self._log_driver._remove_acls_log([pg_dict], self._nb_ovn.transaction,
292314
log_name)
293315
info_args, _info_kwargs = m_info.call_args_list[0]
294-
self.assertIn('Cleared %d (out of %d visited) ACLs', info_args[0])
316+
self.assertIn('Cleared %d, Not found %d (out of %d visited) ACLs',
317+
info_args[0])
295318
self.assertIn('for network log {}'.format(log_name), info_args[0])
296319
self.assertEqual(len(pg_dict["acls"]) - 1, info_args[1])
297-
self.assertEqual(len(pg_dict["acls"]), info_args[2])
320+
self.assertEqual(len(pg_dict["acls"]) - 4, info_args[2])
321+
self.assertEqual(len(pg_dict["acls"]), info_args[3])
298322
self.assertEqual(len(pg_dict["acls"]) - 1,
299323
self._nb_ovn.db_set.call_count)
300324

0 commit comments

Comments
 (0)