From: Andy Zhou <az...@nicira.com> Add functions that can generate "update2" notification for a "monitor2" session. "monitor2" and "update2" are RFC 7047 extensions described by ovsdb-server(1) manpage. See the manpage changes for more details.
Signed-off-by: Andy Zhou <az...@nicira.com> --- v1->v2: * remove ovsdb_monitor_compose_update2() function * change ovsdb_monitor_compose_update() to take a compose_row_update() call back function pointer, so this function can be common for generating both "update" and "update2" message. * rafactor ovsdb_monitor_compse_row_update() and the ..update2() functions, added a common function ovsdb_monitor_row_skip_update() that both functions can share -- remove duplicated code. * Manpage update --- ovsdb/jsonrpc-server.c | 4 +- ovsdb/monitor.c | 145 +++++++++++++++++++++++++++++++++++++++--------- ovsdb/monitor.h | 13 ++++- ovsdb/ovsdb-server.1.in | 84 ++++++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 27 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 daf69cd..f08607a 100644 --- a/ovsdb/monitor.c +++ b/ovsdb/monitor.c @@ -119,6 +119,11 @@ struct ovsdb_monitor_table { struct hmap changes; }; +typedef struct json * +(*compose_row_update_cb_func)(const struct ovsdb_monitor_table *mt, + const struct ovsdb_monitor_row *row, + bool initial, unsigned long int *changed); + static void ovsdb_monitor_destroy(struct ovsdb_monitor *dbmon); static struct ovsdb_monitor_changes * ovsdb_monitor_table_add_changes( struct ovsdb_monitor_table *mt, uint64_t next_txn); @@ -464,6 +469,37 @@ ovsdb_monitor_row_update_type(bool initial, const bool old, const bool new) : !new ? OJMS_DELETE : OJMS_MODIFY; } +static bool +ovsdb_monitor_row_skip_update(const struct ovsdb_monitor_table *mt, + const struct ovsdb_monitor_row *row, + enum ovsdb_monitor_selection type, + unsigned long int *changed) +{ + if (!(mt->select & type)) { + return true; + } + + if (type == OJMS_MODIFY) { + size_t i, 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 true; + } + } + + return false; +} /* Returns JSON for a <row-update> (as described in RFC 7047) for 'row' within * 'mt', or NULL if no row update should be sent. @@ -486,29 +522,10 @@ ovsdb_monitor_compose_row_update( size_t i; type = ovsdb_monitor_row_update_type(initial, row->old, row->new); - if (!(mt->select & type)) { + if (ovsdb_monitor_row_skip_update(mt, row, type, changed)) { 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_json = json_object_create(); old_json = new_json = NULL; if (type & (OJMS_DELETE | OJMS_MODIFY)) { @@ -545,6 +562,76 @@ 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 (ovsdb_monitor_row_skip_update(mt, row, type, changed)) { + 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; + } + + ovsdb_datum_diff(&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); + } 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) { @@ -565,7 +652,8 @@ ovsdb_monitor_max_columns(struct ovsdb_monitor *dbmon) * 'transaction'. */ static struct json* ovsdb_monitor_compose_update(struct ovsdb_monitor *dbmon, - bool initial, uint64_t transaction) + bool initial, uint64_t transaction, + compose_row_update_cb_func row_update) { struct shash_node *node; struct json *json; @@ -587,8 +675,7 @@ ovsdb_monitor_compose_update(struct ovsdb_monitor *dbmon, HMAP_FOR_EACH_SAFE (row, next, hmap_node, &changes->rows) { struct json *row_json; - row_json = ovsdb_monitor_compose_row_update( - mt, row, initial, changed); + row_json = (*row_update)(mt, row, initial, changed); if (row_json) { char uuid[UUID_LEN + 1]; @@ -623,7 +710,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; @@ -637,7 +725,14 @@ 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, + ovsdb_monitor_compose_row_update); + } else { + ovs_assert(version == OVSDB_MONITOR_V2); + json = ovsdb_monitor_compose_update(dbmon, initial, prev_txn, + ovsdb_monitor_compose_row_update2); + } 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..6c85729 100644 --- a/ovsdb/ovsdb-server.1.in +++ b/ovsdb/ovsdb-server.1.in @@ -245,6 +245,90 @@ 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). +.IP +The monitor method described in Section 4.1.5 also applies to +monitor2, with the following exceptions. +. +.RS +.IP \(bu +RPC request method becomes "monitor2". +.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 +. +.IP +Both monitor and monitor2 sessions can exist concurrently. However, +monitor and monitor2 shares the same <json-value> parameter space; it +must be unique among all monitor and monitor2 sessions. +. +.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 notification 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 that maps from row's UUID to a <row-update2> object. A <row-update2> is an object with one of the following members: +. +.RS +.IP "\(dqinitial\(dq: <row>" +present for "initial" updates +.IP "\(dqinsert\(dq: <row>" +present for "insert" updates +.IP "\(dqdelete\(dq: <row>" +present for "delete" updates +.IP "\(dqmodify\(dq: <row>" +present for "modify" updates +.RE +. +.IP +The format of <row> is described in Section 5.1. +. +.IP +<row> is always a null object for a "delete" update. In "initial" and +"insert" updates, <row> omits columns whose values equal the default +value of the column type. +. +.IP +For a "modify" update, <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, plus the key-value pairs whose keys +appear in both maps but with different values. For the latter +elements, <row> includes the value from the new column. +. +.IP +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