ovsdb-idl: Fix atomicity of writes that don't change a column's value.
[sliver-openvswitch.git] / ovsdb / jsonrpc-server.c
index a59a590..fc9c604 100644 (file)
@@ -53,6 +53,9 @@ static void ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *);
 static void ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *);
 static void ovsdb_jsonrpc_session_set_all_options(
     struct ovsdb_jsonrpc_remote *, const struct ovsdb_jsonrpc_options *);
+static void ovsdb_jsonrpc_session_get_status(
+    const struct ovsdb_jsonrpc_remote *,
+    struct shash *);
 
 /* Triggers. */
 static void ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *,
@@ -194,6 +197,21 @@ ovsdb_jsonrpc_server_del_remote(struct shash_node *node)
     free(remote);
 }
 
+void
+ovsdb_jsonrpc_server_get_remote_status(const struct ovsdb_jsonrpc_server *svr,
+                                       struct shash *statuses)
+{
+    struct shash_node *node;
+
+    shash_init(statuses);
+
+    SHASH_FOR_EACH (node, &svr->remotes) {
+        const struct ovsdb_jsonrpc_remote *remote = node->data;
+
+        ovsdb_jsonrpc_session_get_status(remote, statuses);
+    }
+}
+
 /* Forces all of the JSON-RPC sessions managed by 'svr' to disconnect and
  * reconnect. */
 void
@@ -420,6 +438,46 @@ ovsdb_jsonrpc_session_set_all_options(
     }
 }
 
+static void
+ovsdb_jsonrpc_session_get_status(const struct ovsdb_jsonrpc_remote *remote,
+                                 struct shash *shash)
+{
+    const struct ovsdb_jsonrpc_session *s;
+    const struct jsonrpc_session *js;
+    const char *name;
+    struct ovsdb_jsonrpc_remote_status *status;
+    struct reconnect_stats rstats;
+
+    /* We only look at the first session in the list. There should be only one
+     * node in the list for outbound connections. We don't track status for
+     * each individual inbound connection if someone configures the DB that
+     * way. Since outbound connections are the norm, this is fine. */
+    if (list_is_empty(&remote->sessions)) {
+        return;
+    }
+    s = CONTAINER_OF(remote->sessions.next, struct ovsdb_jsonrpc_session, node);
+    js = s->js;
+    if (!js) {
+        return;
+    }
+    name = jsonrpc_session_get_name(js);
+
+    status = xzalloc(sizeof *status);
+    shash_add(shash, name, status);
+
+    status->is_connected = jsonrpc_session_is_connected(js);
+    status->last_error = jsonrpc_session_get_status(js);
+
+    jsonrpc_session_get_reconnect_stats(js, &rstats);
+    status->state = rstats.state;
+    status->sec_since_connect = rstats.msec_since_connect == UINT_MAX
+        ? UINT_MAX : rstats.msec_since_connect / 1000;
+    status->sec_since_disconnect = rstats.msec_since_disconnect == UINT_MAX
+        ? UINT_MAX : rstats.msec_since_disconnect / 1000;
+
+    return;
+}
+
 static const char *
 get_db_name(const struct ovsdb_jsonrpc_session *s)
 {
@@ -980,6 +1038,24 @@ struct ovsdb_jsonrpc_monitor_aux {
     struct json *table_json;    /* JSON for table's transaction. */
 };
 
+static bool
+any_reportable_change(const struct ovsdb_jsonrpc_monitor_table *mt,
+                      const unsigned long int *changed)
+{
+    size_t i;
+
+    for (i = 0; i < mt->n_columns; i++) {
+        const struct ovsdb_jsonrpc_monitor_column *c = &mt->columns[i];
+        unsigned int idx = c->column->index;
+
+        if (c->select & OJMS_MODIFY && bitmap_is_set(changed, idx)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 static bool
 ovsdb_jsonrpc_monitor_change_cb(const struct ovsdb_row *old,
                                 const struct ovsdb_row *new,
@@ -993,7 +1069,6 @@ ovsdb_jsonrpc_monitor_change_cb(const struct ovsdb_row *old,
     struct json *old_json, *new_json;
     struct json *row_json;
     char uuid[UUID_LEN + 1];
-    int n_changed;
     size_t i;
 
     if (!aux->mt || table != aux->mt->table) {
@@ -1016,13 +1091,22 @@ ovsdb_jsonrpc_monitor_change_cb(const struct ovsdb_row *old,
         return true;
     }
 
+    if (type == OJMS_MODIFY && !any_reportable_change(aux->mt, changed)) {
+        /* Nothing of interest changed. */
+        return true;
+    }
+
     old_json = new_json = NULL;
-    n_changed = 0;
+    if (type & (OJMS_DELETE | OJMS_MODIFY)) {
+        old_json = json_object_create();
+    }
+    if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) {
+        new_json = json_object_create();
+    }
     for (i = 0; i < aux->mt->n_columns; i++) {
         const struct ovsdb_jsonrpc_monitor_column *c = &aux->mt->columns[i];
         const struct ovsdb_column *column = c->column;
         unsigned int idx = c->column->index;
-        bool column_changed = false;
 
         if (!(type & c->select)) {
             /* We don't care about this type of change for this particular
@@ -1030,33 +1114,18 @@ ovsdb_jsonrpc_monitor_change_cb(const struct ovsdb_row *old,
             continue;
         }
 
-        if (type == OJMS_MODIFY) {
-            column_changed = bitmap_is_set(changed, idx);
-            n_changed += column_changed;
-        }
-        if (column_changed || type == OJMS_DELETE) {
-            if (!old_json) {
-                old_json = json_object_create();
-            }
+        if ((type == OJMS_MODIFY && bitmap_is_set(changed, idx))
+            || type == OJMS_DELETE) {
             json_object_put(old_json, column->name,
                             ovsdb_datum_to_json(&old->fields[idx],
                                                 &column->type));
         }
         if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) {
-            if (!new_json) {
-                new_json = json_object_create();
-            }
             json_object_put(new_json, column->name,
                             ovsdb_datum_to_json(&new->fields[idx],
                                                 &column->type));
         }
     }
-    if ((type == OJMS_MODIFY && !n_changed) || (!old_json && !new_json)) {
-        /* No reportable changes. */
-        json_destroy(old_json);
-        json_destroy(new_json);
-        return true;
-    }
 
     /* Create JSON object for transaction overall. */
     if (!aux->json) {