2
0
mirror of https://github.com/openvswitch/ovs synced 2025-09-01 06:45:17 +00:00

Allow subclasses of Idl to define a notification hook

It is useful to make the notification events that Idl processes
accessible to users of the library. This will make it possible to
keep external systems in sync, but does not impose any particular
notification pattern.

The Row.from_json() call is added to be able to convert the 'old'
JSON response on an update to a Row object to make it easy for
users of notify() to see what changed, though this usage of Row
is quite different than Idl's typical use.

Signed-off-by: Terry Wilson <twilson@redhat.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
This commit is contained in:
Terry Wilson
2015-04-25 14:57:44 -05:00
committed by Ben Pfaff
parent 596be0ade6
commit d7d417fcdd
3 changed files with 75 additions and 1 deletions

View File

@@ -26,8 +26,12 @@ vlog = ovs.vlog.Vlog("idl")
__pychecker__ = 'no-classattr no-objattrs'
ROW_CREATE = "create"
ROW_UPDATE = "update"
ROW_DELETE = "delete"
class Idl:
class Idl(object):
"""Open vSwitch Database Interface Definition Language (OVSDB IDL).
The OVSDB IDL maintains an in-memory replica of a database. It issues RPC
@@ -264,6 +268,17 @@ class Idl:
self.lock_name = lock_name
self.__send_lock_request()
def notify(self, event, row, updates=None):
"""Hook for implementing create/update/delete notifications
:param event: The event that was triggered
:type event: ROW_CREATE, ROW_UPDATE, or ROW_DELETE
:param row: The row as it is after the operation has occured
:type row: Row
:param updates: For updates, a Row object with just the changed columns
:type updates: Row
"""
def __clear(self):
changed = False
@@ -386,6 +401,7 @@ class Idl:
if row:
del table.rows[uuid]
changed = True
self.notify(ROW_DELETE, row)
else:
# XXX rate-limit
vlog.warn("cannot delete missing row %s from table %s"
@@ -401,15 +417,19 @@ class Idl:
% (uuid, table.name))
if self.__row_update(table, row, new):
changed = True
self.notify(ROW_CREATE, row)
else:
op = ROW_UPDATE
if not row:
row = self.__create_row(table, uuid)
changed = True
op = ROW_CREATE
# XXX rate-limit
vlog.warn("cannot modify missing row %s in table %s"
% (uuid, table.name))
if self.__row_update(table, row, new):
changed = True
self.notify(op, row, Row.from_json(self, table, uuid, old))
return changed
def __row_update(self, table, row, row_json):
@@ -570,6 +590,26 @@ class Row(object):
return
self._idl.txn._write(self, column, datum)
@classmethod
def from_json(cls, idl, table, uuid, row_json):
data = {}
for column_name, datum_json in row_json.iteritems():
column = table.columns.get(column_name)
if not column:
# XXX rate-limit
vlog.warn("unknown column %s in table %s"
% (column_name, table.name))
continue
try:
datum = ovs.db.data.Datum.from_json(column.type, datum_json)
except error.Error, e:
# XXX rate-limit
vlog.warn("error parsing column %s in table %s: %s"
% (column_name, table.name, e))
continue
data[column_name] = datum
return cls(idl, table, uuid, data)
def verify(self, column_name):
"""Causes the original contents of column 'column_name' in this row to
be verified as a prerequisite to completing the transaction. That is,

View File

@@ -497,6 +497,23 @@ OVSDB_CHECK_IDL_PY([getattr idl, insert ops],
003: done
]])
OVSDB_CHECK_IDL_PY([row-from-json idl, whats this],
[['["idltest",
{"op": "insert",
"table": "simple",
"row": {"i": 1}},
{"op": "insert",
"table": "simple",
"row": {}}]']],
[['notifytest insert 2, notifytest set 1 b 1, notifytest delete 0']],
[[000: i=0 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<1>
000: i=1 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2>
001: commit, status=success, events=create|2|None, delete|0|None, update|1|b
002: i=1 r=0 b=true s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<2>
002: i=2 r=0 b=false s= u=<0> ia=[] ra=[] ba=[] sa=[] ua=[] uuid=<3>
003: done
]])
AT_SETUP([idl handling of missing tables and columns - C])
AT_KEYWORDS([ovsdb server idl positive])
OVS_RUNDIR=`pwd`; export OVS_RUNDIR

View File

@@ -228,11 +228,24 @@ def idltest_find_simple(idl, i):
def idl_set(idl, commands, step):
txn = ovs.db.idl.Transaction(idl)
increment = False
events = []
for command in commands.split(','):
words = command.split()
name = words[0]
args = words[1:]
if name == "notifytest":
name = args[0]
args = args[1:]
old_notify = idl.notify
def notify(event, row, updates=None):
upcol = updates._data.keys()[0] if updates else None
events.append("%s|%s|%s" % (event, row.i, upcol))
idl.notify = old_notify
idl.notify = notify
if name == "set":
if len(args) != 3:
sys.stderr.write('"set" command requires 3 arguments\n')
@@ -338,6 +351,10 @@ def idl_set(idl, commands, step):
% (step, ovs.db.idl.Transaction.status_to_string(status)))
if increment and status == ovs.db.idl.Transaction.SUCCESS:
sys.stdout.write(", increment=%d" % txn.get_increment_new_value())
if events:
# Event notifications from operations in a single transaction are
# not in a gauranteed order due to update messages being dicts
sys.stdout.write(", events=" + ", ".join(sorted(events)))
sys.stdout.write("\n")
sys.stdout.flush()