2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 14:25:26 +00:00

python: Send notifications after the transaction ends.

The Python IDL notification mechanism was sending a notification
for each processed update in a transaction as it was processed.
This causes issues with multi-row changes that contain references
to each other.

For example, if a Logical_Router_Port is created along with a
Gateway_Chassis, and the LRP.gateway_chassis set to that GC, then
when the notify() passes the CREATE event for the LRP, the GC will
not yet have been processed, so __getattr__ when _uuid_to_row fails
to find the GC, will return the default value for LRP.gateway_chassis
which is [].

This patch has the process_update methods return the notifications
that would be produced when a row changes, so they can be queued
and sent after all rows have been processed.

Fixes: d7d417fcdd ("Allow subclasses of Idl to define a notification hook")
Signed-off-by: Terry Wilson <twilson@redhat.com>
Acked-by: Brian Haley <haleyb.dev@gmail.com>
Acked-by: Dumitru Ceara <dceara@redhat.com>
Tested-by: Flavio Fernandes <flavio@flaviof.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
This commit is contained in:
Terry Wilson
2021-03-09 14:34:16 +00:00
committed by Ilya Maximets
parent cdaa7e0fd6
commit 64b8c1d9ad
3 changed files with 52 additions and 17 deletions

View File

@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import collections
import functools
import uuid
@@ -39,6 +40,10 @@ OVSDB_UPDATE2 = 1
CLUSTERED = "clustered"
Notice = collections.namedtuple('Notice', ('event', 'row', 'updates'))
Notice.__new__.__defaults__ = (None,) # default updates=None
class Idl(object):
"""Open vSwitch Database Interface Definition Language (OVSDB IDL).
@@ -614,6 +619,7 @@ class Idl(object):
raise error.Error("<table-updates> is not an object",
table_updates)
notices = []
for table_name, table_update in table_updates.items():
table = tables.get(table_name)
if not table:
@@ -639,7 +645,9 @@ class Idl(object):
% (table_name, uuid_string))
if version == OVSDB_UPDATE2:
if self.__process_update2(table, uuid, row_update):
changes = self.__process_update2(table, uuid, row_update)
if changes:
notices.append(changes)
self.change_seqno += 1
continue
@@ -652,17 +660,20 @@ class Idl(object):
raise error.Error('<row-update> missing "old" and '
'"new" members', row_update)
if self.__process_update(table, uuid, old, new):
changes = self.__process_update(table, uuid, old, new)
if changes:
notices.append(changes)
self.change_seqno += 1
for notice in notices:
self.notify(*notice)
def __process_update2(self, table, uuid, row_update):
"""Returns Notice if a column changed, False otherwise."""
row = table.rows.get(uuid)
changed = False
if "delete" in row_update:
if row:
del table.rows[uuid]
self.notify(ROW_DELETE, row)
changed = True
return Notice(ROW_DELETE, row)
else:
# XXX rate-limit
vlog.warn("cannot delete missing row %s from table"
@@ -681,29 +692,27 @@ class Idl(object):
changed = self.__row_update(table, row, row_update)
table.rows[uuid] = row
if changed:
self.notify(ROW_CREATE, row)
return Notice(ROW_CREATE, row)
elif "modify" in row_update:
if not row:
raise error.Error('Modify non-existing row')
old_row = self.__apply_diff(table, row, row_update['modify'])
self.notify(ROW_UPDATE, row, Row(self, table, uuid, old_row))
changed = True
return Notice(ROW_UPDATE, row, Row(self, table, uuid, old_row))
else:
raise error.Error('<row-update> unknown operation',
row_update)
return changed
return False
def __process_update(self, table, uuid, old, new):
"""Returns True if a column changed, False otherwise."""
"""Returns Notice if a column changed, False otherwise."""
row = table.rows.get(uuid)
changed = False
if not new:
# Delete row.
if row:
del table.rows[uuid]
changed = True
self.notify(ROW_DELETE, row)
return Notice(ROW_DELETE, row)
else:
# XXX rate-limit
vlog.warn("cannot delete missing row %s from table %s"
@@ -723,7 +732,7 @@ class Idl(object):
if op == ROW_CREATE:
table.rows[uuid] = row
if changed:
self.notify(ROW_CREATE, row)
return Notice(ROW_CREATE, row)
else:
op = ROW_UPDATE
if not row:
@@ -737,8 +746,8 @@ class Idl(object):
if op == ROW_CREATE:
table.rows[uuid] = row
if changed:
self.notify(op, row, Row.from_json(self, table, uuid, old))
return changed
return Notice(op, row, Row.from_json(self, table, uuid, old))
return False
def __check_server_db(self):
"""Returns True if this is a valid server database, False otherwise."""