Avoid race condition when processing RowEvents

In https://review.opendev.org/#/c/753824/ ovsdbapp adds the ability
to pass a "frozen" object to the RowEventHandler so that if a
transaction is started from the main thread that changes the row,
it won't step on the values that the Event is trying to process.

This patch copies in the relevant code from newer ovsdbapp ond ovs
to avoid bumping their required versions in a stable branch.

Change-Id: I87489596e2ff224431f7e83f43a1725172ee0953
Related-Bug: #1896816
(cherry picked from commit ab6d4afd95)
This commit is contained in:
Terry Wilson 2020-09-23 20:32:33 +00:00
parent df9ef3df03
commit 7ce3c8e1f1
2 changed files with 67 additions and 3 deletions

View File

@ -0,0 +1,64 @@
# Copyright 2021 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# We don't technically require ovs 2.10 or ovsdbapp that has these fixes so
# just include them here for stable releases
try:
from ovs.db import custom_index
IndexEntryClass = custom_index.IndexEntryClass
except ImportError:
import collections
from ovs.db import data
def IndexEntryClass(table):
def defaults_uuid_to_row(atom, base):
return atom.value
columns = ['uuid'] + list(table.columns.keys())
cls = collections.namedtuple(table.name, columns)
cls._table = table
cls.__new__.__defaults__ = (None,) + tuple(
data.Datum.default(c.type).to_python(defaults_uuid_to_row)
for c in table.columns.values())
return cls
try:
from ovsdbapp.backend.ovs_idl import idlutils
frozen_row = idlutils.frozen_row
except AttributeError:
def frozen_row(row):
try:
IndexEntry = row._table.rows.IndexEntry
except AttributeError:
row._table.rows = IndexEntryClass(row._table)
return IndexEntry(
uuid=row.uuid,
**{col: getattr(row, col)
for col in row._table.columns if hasattr(row, col)})
try:
from ovsdbapp.backend.ovs_idl import event as row_event
from ovsdbapp import event as ovsdb_event
RowEventHandler = row_event.RowEventHandler
except AttributeError:
class RowEventHandler(ovsdb_event.RowEventHandler):
def notify(self, event, row, updates=None):
row = frozen_row(row)
super().notify(event, row, updates)

View File

@ -26,7 +26,6 @@ from ovs.stream import Stream
from ovsdbapp.backend.ovs_idl import connection
from ovsdbapp.backend.ovs_idl import event as row_event
from ovsdbapp.backend.ovs_idl import idlutils
from ovsdbapp import event
from neutron.common.ovn import constants as ovn_const
from neutron.common.ovn import exceptions
@ -34,6 +33,7 @@ from neutron.common.ovn import hash_ring_manager
from neutron.common.ovn import utils
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
from neutron.db import ovn_hash_ring_db
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import backports
CONF = cfg.CONF
@ -346,7 +346,7 @@ class FIPAddDeleteEvent(row_event.RowEvent):
self.driver.delete_mac_binding_entries(row.external_ip)
class OvnDbNotifyHandler(event.RowEventHandler):
class OvnDbNotifyHandler(backports.RowEventHandler):
def __init__(self, driver):
super(OvnDbNotifyHandler, self).__init__()
self.driver = driver
@ -362,7 +362,7 @@ class Ml2OvnIdlBase(connection.OvsdbIdl):
class BaseOvnIdl(Ml2OvnIdlBase):
def __init__(self, remote, schema):
self.notify_handler = event.RowEventHandler()
self.notify_handler = backports.RowEventHandler()
super(BaseOvnIdl, self).__init__(remote, schema)
@classmethod