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

Reply via email to