Add functions that can generate "update2" notification for a "monitor2" session. "monitor2" and "update2" are RFC 7047 extensions deescribed by ovsdb-server(1) manpage. See the manpage changes for more details.
Signed-off-by: Andy Zhou <az...@nicira.com> --- ovsdb/jsonrpc-server.c | 4 +- ovsdb/monitor.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++- ovsdb/monitor.h | 13 +++- ovsdb/ovsdb-server.1.in | 81 +++++++++++++++++++++++++ 4 files changed, 248 insertions(+), 4 deletions(-) diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c index fffcb73..729b368 100644 --- a/ovsdb/jsonrpc-server.c +++ b/ovsdb/jsonrpc-server.c @@ -22,6 +22,7 @@ #include "bitmap.h" #include "column.h" #include "dynamic-string.h" +#include "monitor.h" #include "json.h" #include "jsonrpc.h" #include "ovsdb-error.h" @@ -1294,7 +1295,8 @@ static struct json * ovsdb_jsonrpc_monitor_compose_update(struct ovsdb_jsonrpc_monitor *m, bool initial) { - return ovsdb_monitor_get_update(m->dbmon, initial, &m->unflushed); + return ovsdb_monitor_get_update(m->dbmon, initial, &m->unflushed, + OVSDB_MONITOR_V1); } static bool diff --git a/ovsdb/monitor.c b/ovsdb/monitor.c index f978960..aef7f61 100644 --- a/ovsdb/monitor.c +++ b/ovsdb/monitor.c @@ -547,6 +547,96 @@ ovsdb_monitor_compose_row_update( return row_json; } +/* Returns JSON for a <row-update2> (as described in ovsdb-server(1) mapage) + * for 'row' within * 'mt', or NULL if no row update should be sent. + * + * The caller should specify 'initial' as true if the returned JSON is + * going to be used as part of the initial reply to a "monitor2" request, + * false if it is going to be used as part of an "update2" notification. + * + * 'changed' must be a scratch buffer for internal use that is at least + * bitmap_n_bytes(mt->n_columns) bytes long. */ +static struct json * +ovsdb_monitor_compose_row_update2( + const struct ovsdb_monitor_table *mt, + const struct ovsdb_monitor_row *row, + bool initial, unsigned long int *changed) +{ + enum ovsdb_monitor_selection type; + struct json *row_update2, *diff_json; + size_t i; + + type = ovsdb_monitor_row_update_type(initial, row->old, row->new); + if (!(mt->select & type)) { + return NULL; + } + + if (type == OJMS_MODIFY) { + size_t n_changes; + + n_changes = 0; + memset(changed, 0, bitmap_n_bytes(mt->n_columns)); + for (i = 0; i < mt->n_columns; i++) { + const struct ovsdb_column *c = mt->columns[i].column; + if (!ovsdb_datum_equals(&row->old[i], &row->new[i], &c->type)) { + bitmap_set1(changed, i); + n_changes++; + } + } + if (!n_changes) { + /* No actual changes: presumably a row changed and then + * changed back later. */ + return NULL; + } + } + + row_update2 = json_object_create(); + if (type == OJMS_DELETE) { + json_object_put(row_update2, "delete", json_null_create()); + } else { + diff_json = json_object_create(); + const char *op; + + for (i = 0; i < mt->n_columns; i++) { + const struct ovsdb_monitor_column *c = &mt->columns[i]; + + if (!(type & c->select)) { + /* We don't care about this type of change for this + * particular column (but we will care about it for some + * other column). */ + continue; + } + + if (type == OJMS_MODIFY) { + struct ovsdb_datum *diff; + + if (!bitmap_is_set(changed, i)) { + continue; + } + + diff = ovsdb_datum_diff(&row->old[i], &row->new[i], + &c->column->type); + json_object_put(diff_json, c->column->name, + ovsdb_datum_to_json(diff, &c->column->type)); + ovsdb_datum_destroy(diff, &c->column->type); + free(diff); + } else { + if (!ovsdb_datum_is_default(&row->new[i], &c->column->type)) { + json_object_put(diff_json, c->column->name, + ovsdb_datum_to_json(&row->new[i], + &c->column->type)); + } + } + } + + op = type == OJMS_INITIAL ? "initial" + : type == OJMS_MODIFY ? "modify" : "insert"; + json_object_put(row_update2, op, diff_json); + } + + return row_update2; +} + static size_t ovsdb_monitor_max_columns(struct ovsdb_monitor *dbmon) { @@ -616,6 +706,60 @@ ovsdb_monitor_compose_update(struct ovsdb_monitor *dbmon, return json; } +/* Constructs and returns JSON for a <table-updates2> object (as described + * in ovsdb-server(1) man page) for all the outstanding changes + * within 'monitor2', starting from 'transaction'. */ +static struct json* +ovsdb_monitor_compose_update2(struct ovsdb_monitor *dbmon, + bool initial, uint64_t transaction) +{ + struct shash_node *node; + struct json *json; + size_t max_columns = ovsdb_monitor_max_columns(dbmon); + unsigned long int *changed = xmalloc(bitmap_n_bytes(max_columns)); + + json = NULL; + SHASH_FOR_EACH (node, &dbmon->tables) { + struct ovsdb_monitor_table *mt = node->data; + struct ovsdb_monitor_row *row, *next; + struct ovsdb_monitor_changes *changes; + struct json *table_json = NULL; + + changes = ovsdb_monitor_table_find_changes(mt, transaction); + if (!changes) { + continue; + } + + HMAP_FOR_EACH_SAFE (row, next, hmap_node, &changes->rows) { + struct json *row_json; + + row_json = ovsdb_monitor_compose_row_update2( + mt, row, initial, changed); + if (row_json) { + char uuid[UUID_LEN + 1]; + + /* Create JSON object for transaction overall. */ + if (!json) { + json = json_object_create(); + } + + /* Create JSON object for transaction on this table. */ + if (!table_json) { + table_json = json_object_create(); + json_object_put(json, mt->table->schema->name, table_json); + } + + /* Add JSON row to JSON table. */ + snprintf(uuid, sizeof uuid, UUID_FMT, UUID_ARGS(&row->uuid)); + json_object_put(table_json, uuid, row_json); + } + } + } + free(changed); + + return json; +} + /* Returns JSON for a <table-updates> object (as described in RFC 7047) * for all the outstanding changes within 'monitor' that starts from * '*unflushed' transaction id. @@ -625,7 +769,8 @@ ovsdb_monitor_compose_update(struct ovsdb_monitor *dbmon, * going to be used as part of an "update" notification. */ struct json * ovsdb_monitor_get_update(struct ovsdb_monitor *dbmon, - bool initial, uint64_t *unflushed) + bool initial, uint64_t *unflushed, + enum ovsdb_monitor_version version) { struct ovsdb_monitor_json_cache_node *cache_node; struct shash_node *node; @@ -639,7 +784,12 @@ ovsdb_monitor_get_update(struct ovsdb_monitor *dbmon, if (cache_node) { json = cache_node->json ? json_clone(cache_node->json) : NULL; } else { - json = ovsdb_monitor_compose_update(dbmon, initial, prev_txn); + if (version == OVSDB_MONITOR_V1) { + json = ovsdb_monitor_compose_update(dbmon, initial, prev_txn); + } else { + ovs_assert(version == OVSDB_MONITOR_V2); + json = ovsdb_monitor_compose_update2(dbmon, initial, prev_txn); + } ovsdb_monitor_json_cache_insert(dbmon, prev_txn, json); } diff --git a/ovsdb/monitor.h b/ovsdb/monitor.h index a8e5310..b1a5146 100644 --- a/ovsdb/monitor.h +++ b/ovsdb/monitor.h @@ -27,6 +27,15 @@ enum ovsdb_monitor_selection { }; +enum ovsdb_monitor_version { + OVSDB_MONITOR_V1, /* RFC 7047 "monitor" method. */ + OVSDB_MONITOR_V2, /* Extension to RFC 7047, see ovsdb-server + man page for details. */ + + /* Last entry. */ + OVSDB_MONITOR_VERSION_MAX +}; + struct ovsdb_monitor *ovsdb_monitor_create(struct ovsdb *db, struct ovsdb_jsonrpc_monitor *jsonrpc_monitor); @@ -55,7 +64,9 @@ ovsdb_monitor_table_check_duplicates(struct ovsdb_monitor *, const struct ovsdb_table *); struct json *ovsdb_monitor_get_update(struct ovsdb_monitor *dbmon, - bool initial, uint64_t *unflushed_transaction); + bool initial, + uint64_t *unflushed_transaction, + enum ovsdb_monitor_version version); void ovsdb_monitor_table_add_select(struct ovsdb_monitor *dbmon, const struct ovsdb_table *table, diff --git a/ovsdb/ovsdb-server.1.in b/ovsdb/ovsdb-server.1.in index e340993..1d68fd6 100644 --- a/ovsdb/ovsdb-server.1.in +++ b/ovsdb/ovsdb-server.1.in @@ -245,6 +245,87 @@ notifications (see below) to the request, it must be unique among all active monitors. \fBovsdb\-server\fR rejects attempt to create two monitors with the same identifier. . +.IP "4.1.12. Monitor2" +A new monitor method added in Open vSwitch version 2.5. Monitor2 allows +for more efficient update notifications (described below). + +Monitor method described in Section 4.1.5 also applies to monitor2, with +the following exceptions. + +.RS +.IP \(bu +RPC request method becomes "montior2" +.IP \(bu +Replay result follows <table-updates2>, described in Section 4.1.13. +.IP \(bu +Subsequent changes are sent to the client using the "update2" monitor +notification, described in Section 4.1.13 +.RE + +Both monitor and monitor2 sessions can exist concurrently. However, +monitor and monitor2 shares the smae <json-value> parameter space; it +must be unique among all monitor and monitor2 seesion. + +.IP "4.1.13. Update2 notification" +The "update2" notification is sent by the server to the client to report +changes in tables that are being monitored following a "monitor2" request +as described above. The notifaction has the following members: + +.RS +.nf +"method": "update2" +"params": [<json-value>, <table-updates2>] +"id": NULL +.fi +.RE + +.IP +The <json-value> in "params" is the same as the value passed as the +<json-value> in "params" for the corresponding "monitor" request. +<table-updates2> is an object that maps from a table name to a <table-update2>. +A <table-update2> is an object htat maps from row's UUID to a <row-update2> object. A <row-update2> is an object with one of the following members: + +.RS +.nf +"initial": <row> present for "initial' updates +"insert": <row> present for "insert' updates +"delete": <row> present for "delete' updates +"modify": <row> present for "modify' updates +.fi +.RE + +.IP +The format of <row> is described in Section 5.1. + +.IP +<row> is always a NULL object with a "delete" object. In "initial" and +"insert" objects, <row> omits columns whose values equal to default +value of the column type. + +.IP +In "modify" object, <row> contains only the columns that are modified. +<row> stores the difference between the old and new value for those columns, +as described below. + +.IP +For columns with single value, the difference is the value of the new +column. + +.IP +The difference between two sets are all elements that only belong +to one of the sets. + +.IP +The difference between two maps are all key value pairs whose keys appears +in only one of the maps. And elements with overlapping +keys between the two maps, but are associated with different values. +For those elements, <row> stores the key value pair from the new column. + +.IP +Note that initial views of rows are not presented in update2 notifications, +but in the response object to the monitor2 request. The formatting of the +<table-updates2> object, however, is the same in either case. + .IP "5.1. Notation" For <condition>, RFC 7047 only allows the use of \fB!=\fR, \fB==\fR, \fBincludes\fR, and \fBexcludes\fR operators with set types. Open -- 1.9.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev