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:
committed by
Ilya Maximets
parent
cdaa7e0fd6
commit
64b8c1d9ad
@@ -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."""
|
||||
|
Reference in New Issue
Block a user