From 52553aeaa95ab46a1c120a8628621d8ed390a91f Mon Sep 17 00:00:00 2001 From: Andy Zhou Date: Thu, 15 Oct 2015 14:07:43 -0700 Subject: [PATCH] ovsdb: generate update2 notification for a monitor2 session 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 Acked-by: Ben Pfaff --- 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 fffcb731d..729b36819 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 daf69cd42..f08607aa3 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 (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 (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 a8e531012..b1a5146b6 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 e340993f7..6c857294c 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 , 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 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": [, ] +"id": null +.fi +.RE +. +.IP +The in "params" is the same as the value passed as the + in "params" for the corresponding "monitor" request. + is an object that maps from a table name to a . +A is an object that maps from row's UUID to a object. A is an object with one of the following members: +. +.RS +.IP "\(dqinitial\(dq: " +present for "initial" updates +.IP "\(dqinsert\(dq: " +present for "insert" updates +.IP "\(dqdelete\(dq: " +present for "delete" updates +.IP "\(dqmodify\(dq: " +present for "modify" updates +.RE +. +.IP +The format of is described in Section 5.1. +. +.IP + is always a null object for a "delete" update. In "initial" and +"insert" updates, omits columns whose values equal the default +value of the column type. +. +.IP +For a "modify" update, contains only the columns that are modified. + 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, 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 + object, however, is the same in either case. +. .IP "5.1. Notation" For , RFC 7047 only allows the use of \fB!=\fR, \fB==\fR, \fBincludes\fR, and \fBexcludes\fR operators with set types. Open -- 2.20.1