ovsdb-server: Add support for multiple databases.
authorBen Pfaff <blp@nicira.com>
Fri, 7 Sep 2012 17:07:03 +0000 (10:07 -0700)
committerBen Pfaff <blp@nicira.com>
Tue, 25 Sep 2012 05:39:22 +0000 (22:39 -0700)
The OVSDB protocol has supported multiple databases for a long time, but
the ovsdb-server implementation only supported one database at a time.
This commit adds support for multiple databases.

Feature #12353.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
12 files changed:
ovsdb/jsonrpc-server.c
ovsdb/jsonrpc-server.h
ovsdb/ovsdb-server.1.in
ovsdb/ovsdb-server.c
ovsdb/server.c
ovsdb/server.h
ovsdb/trigger.c
ovsdb/trigger.h
tests/ovsdb-server.at
tests/test-ovsdb.c
utilities/ovs-ctl.8
utilities/ovs-ctl.in

index bb887d0ba10c09ec60254651c8524303c0d93c39..279ea97e6cbb8d952d902e86adb55a5d9f753f65 100644 (file)
@@ -66,6 +66,7 @@ static void ovsdb_jsonrpc_session_unlock__(struct ovsdb_lock_waiter *);
 
 /* Triggers. */
 static void ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *,
 
 /* Triggers. */
 static void ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *,
+                                         struct ovsdb *,
                                          struct json *id, struct json *params);
 static struct ovsdb_jsonrpc_trigger *ovsdb_jsonrpc_trigger_find(
     struct ovsdb_jsonrpc_session *, const struct json *id, size_t hash);
                                          struct json *id, struct json *params);
 static struct ovsdb_jsonrpc_trigger *ovsdb_jsonrpc_trigger_find(
     struct ovsdb_jsonrpc_session *, const struct json *id, size_t hash);
@@ -76,7 +77,7 @@ static void ovsdb_jsonrpc_trigger_complete_done(
 
 /* Monitors. */
 static struct json *ovsdb_jsonrpc_monitor_create(
 
 /* Monitors. */
 static struct json *ovsdb_jsonrpc_monitor_create(
-    struct ovsdb_jsonrpc_session *, struct json *params);
+    struct ovsdb_jsonrpc_session *, struct ovsdb *, struct json *params);
 static struct jsonrpc_msg *ovsdb_jsonrpc_monitor_cancel(
     struct ovsdb_jsonrpc_session *,
     struct json_array *params,
 static struct jsonrpc_msg *ovsdb_jsonrpc_monitor_cancel(
     struct ovsdb_jsonrpc_session *,
     struct json_array *params,
@@ -106,16 +107,29 @@ static struct ovsdb_jsonrpc_remote *ovsdb_jsonrpc_server_add_remote(
 );
 static void ovsdb_jsonrpc_server_del_remote(struct shash_node *);
 
 );
 static void ovsdb_jsonrpc_server_del_remote(struct shash_node *);
 
+/* Creates and returns a new server to provide JSON-RPC access to an OVSDB.
+ *
+ * The caller must call ovsdb_jsonrpc_server_add_db() for each database to
+ * which 'server' should provide access. */
 struct ovsdb_jsonrpc_server *
 struct ovsdb_jsonrpc_server *
-ovsdb_jsonrpc_server_create(struct ovsdb *db)
+ovsdb_jsonrpc_server_create(void)
 {
     struct ovsdb_jsonrpc_server *server = xzalloc(sizeof *server);
 {
     struct ovsdb_jsonrpc_server *server = xzalloc(sizeof *server);
-    ovsdb_server_init(&server->up, db);
+    ovsdb_server_init(&server->up);
     server->max_sessions = 64;
     shash_init(&server->remotes);
     return server;
 }
 
     server->max_sessions = 64;
     shash_init(&server->remotes);
     return server;
 }
 
+/* Adds 'db' to the set of databases served out by 'svr'.  Returns true if
+ * successful, false if 'db''s name is the same as some database already in
+ * 'server'. */
+bool
+ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *svr, struct ovsdb *db)
+{
+    return ovsdb_server_add_db(&svr->up, db);
+}
+
 void
 ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *svr)
 {
 void
 ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *svr)
 {
@@ -350,7 +364,7 @@ ovsdb_jsonrpc_session_create(struct ovsdb_jsonrpc_remote *remote,
     struct ovsdb_jsonrpc_session *s;
 
     s = xzalloc(sizeof *s);
     struct ovsdb_jsonrpc_session *s;
 
     s = xzalloc(sizeof *s);
-    ovsdb_session_init(&s->up, remote->server->up.db);
+    ovsdb_session_init(&s->up, &remote->server->up);
     s->remote = remote;
     list_push_back(&remote->sessions, &s->node);
     hmap_init(&s->triggers);
     s->remote = remote;
     list_push_back(&remote->sessions, &s->node);
     hmap_init(&s->triggers);
@@ -558,21 +572,22 @@ ovsdb_jsonrpc_session_get_status(const struct ovsdb_jsonrpc_remote *remote,
     return true;
 }
 
     return true;
 }
 
-static const char *
-get_db_name(const struct ovsdb_jsonrpc_session *s)
-{
-    return s->remote->server->up.db->schema->name;
-}
-
-static struct jsonrpc_msg *
-ovsdb_jsonrpc_check_db_name(const struct ovsdb_jsonrpc_session *s,
-                            const struct jsonrpc_msg *request)
+/* Examines 'request' to determine the database to which it relates, and then
+ * searches 's' to find that database:
+ *
+ *    - If successful, returns the database and sets '*replyp' to NULL.
+ *
+ *    - If no such database exists, returns NULL and sets '*replyp' to an
+ *      appropriate JSON-RPC error reply, owned by the caller. */
+static struct ovsdb *
+ovsdb_jsonrpc_lookup_db(const struct ovsdb_jsonrpc_session *s,
+                        const struct jsonrpc_msg *request,
+                        struct jsonrpc_msg **replyp)
 {
     struct json_array *params;
 {
     struct json_array *params;
-    const char *want_db_name;
-    const char *have_db_name;
     struct ovsdb_error *error;
     struct ovsdb_error *error;
-    struct jsonrpc_msg *reply;
+    const char *db_name;
+    struct ovsdb *db;
 
     params = json_array(request->params);
     if (!params->n || params->elems[0]->type != JSON_STRING) {
 
     params = json_array(request->params);
     if (!params->n || params->elems[0]->type != JSON_STRING) {
@@ -582,22 +597,23 @@ ovsdb_jsonrpc_check_db_name(const struct ovsdb_jsonrpc_session *s,
         goto error;
     }
 
         goto error;
     }
 
-    want_db_name = params->elems[0]->u.string;
-    have_db_name = get_db_name(s);
-    if (strcmp(want_db_name, have_db_name)) {
+    db_name = params->elems[0]->u.string;
+    db = shash_find_data(&s->up.server->dbs, db_name);
+    if (!db) {
         error = ovsdb_syntax_error(
             request->params, "unknown database",
             "%s request specifies unknown database %s",
         error = ovsdb_syntax_error(
             request->params, "unknown database",
             "%s request specifies unknown database %s",
-            request->method, want_db_name);
+            request->method, db_name);
         goto error;
     }
 
         goto error;
     }
 
-    return NULL;
+    *replyp = NULL;
+    return db;
 
 error:
 
 error:
-    reply = jsonrpc_create_reply(ovsdb_error_to_json(error), request->id);
+    *replyp = jsonrpc_create_reply(ovsdb_error_to_json(error), request->id);
     ovsdb_error_destroy(error);
     ovsdb_error_destroy(error);
-    return reply;
+    return NULL;
 }
 
 static struct ovsdb_error *
 }
 
 static struct ovsdb_error *
@@ -739,10 +755,10 @@ error:
 }
 
 static struct jsonrpc_msg *
 }
 
 static struct jsonrpc_msg *
-execute_transaction(struct ovsdb_jsonrpc_session *s,
+execute_transaction(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
                     struct jsonrpc_msg *request)
 {
                     struct jsonrpc_msg *request)
 {
-    ovsdb_jsonrpc_trigger_create(s, request->id, request->params);
+    ovsdb_jsonrpc_trigger_create(s, db, request->id, request->params);
     request->id = NULL;
     request->params = NULL;
     jsonrpc_msg_destroy(request);
     request->id = NULL;
     request->params = NULL;
     jsonrpc_msg_destroy(request);
@@ -756,30 +772,39 @@ ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,
     struct jsonrpc_msg *reply;
 
     if (!strcmp(request->method, "transact")) {
     struct jsonrpc_msg *reply;
 
     if (!strcmp(request->method, "transact")) {
-        reply = ovsdb_jsonrpc_check_db_name(s, request);
+        struct ovsdb *db = ovsdb_jsonrpc_lookup_db(s, request, &reply);
         if (!reply) {
         if (!reply) {
-            reply = execute_transaction(s, request);
+            reply = execute_transaction(s, db, request);
         }
     } else if (!strcmp(request->method, "monitor")) {
         }
     } else if (!strcmp(request->method, "monitor")) {
-        reply = ovsdb_jsonrpc_check_db_name(s, request);
+        struct ovsdb *db = ovsdb_jsonrpc_lookup_db(s, request, &reply);
         if (!reply) {
             reply = jsonrpc_create_reply(
         if (!reply) {
             reply = jsonrpc_create_reply(
-                ovsdb_jsonrpc_monitor_create(s, request->params), request->id);
+                ovsdb_jsonrpc_monitor_create(s, db, request->params),
+                request->id);
         }
     } else if (!strcmp(request->method, "monitor_cancel")) {
         reply = ovsdb_jsonrpc_monitor_cancel(s, json_array(request->params),
                                              request->id);
     } else if (!strcmp(request->method, "get_schema")) {
         }
     } else if (!strcmp(request->method, "monitor_cancel")) {
         reply = ovsdb_jsonrpc_monitor_cancel(s, json_array(request->params),
                                              request->id);
     } else if (!strcmp(request->method, "get_schema")) {
-        reply = ovsdb_jsonrpc_check_db_name(s, request);
+        struct ovsdb *db = ovsdb_jsonrpc_lookup_db(s, request, &reply);
         if (!reply) {
         if (!reply) {
-            reply = jsonrpc_create_reply(
-                ovsdb_schema_to_json(s->remote->server->up.db->schema),
-                request->id);
+            reply = jsonrpc_create_reply(ovsdb_schema_to_json(db->schema),
+                                         request->id);
         }
     } else if (!strcmp(request->method, "list_dbs")) {
         }
     } else if (!strcmp(request->method, "list_dbs")) {
-        reply = jsonrpc_create_reply(
-            json_array_create_1(json_string_create(get_db_name(s))),
-            request->id);
+        size_t n_dbs = shash_count(&s->up.server->dbs);
+        struct shash_node *node;
+        struct json **dbs;
+        size_t i;
+
+        dbs = xmalloc(n_dbs * sizeof *dbs);
+        i = 0;
+        SHASH_FOR_EACH (node, &s->up.server->dbs) {
+            dbs[i++] = json_string_create(node->name);
+        }
+        reply = jsonrpc_create_reply(json_array_create(dbs, n_dbs),
+                                     request->id);
     } else if (!strcmp(request->method, "lock")) {
         reply = ovsdb_jsonrpc_session_lock(s, request, OVSDB_LOCK_WAIT);
     } else if (!strcmp(request->method, "steal")) {
     } else if (!strcmp(request->method, "lock")) {
         reply = ovsdb_jsonrpc_session_lock(s, request, OVSDB_LOCK_WAIT);
     } else if (!strcmp(request->method, "steal")) {
@@ -836,7 +861,7 @@ struct ovsdb_jsonrpc_trigger {
 };
 
 static void
 };
 
 static void
-ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *s,
+ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
                              struct json *id, struct json *params)
 {
     struct ovsdb_jsonrpc_trigger *t;
                              struct json *id, struct json *params)
 {
     struct ovsdb_jsonrpc_trigger *t;
@@ -858,7 +883,7 @@ ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *s,
 
     /* Insert into trigger table. */
     t = xmalloc(sizeof *t);
 
     /* Insert into trigger table. */
     t = xmalloc(sizeof *t);
-    ovsdb_trigger_init(&s->up, &t->trigger, params, time_msec());
+    ovsdb_trigger_init(&s->up, db, &t->trigger, params, time_msec());
     t->id = id;
     hmap_insert(&s->triggers, &t->hmap_node, hash);
 
     t->id = id;
     hmap_insert(&s->triggers, &t->hmap_node, hash);
 
@@ -962,6 +987,7 @@ struct ovsdb_jsonrpc_monitor_table {
 struct ovsdb_jsonrpc_monitor {
     struct ovsdb_replica replica;
     struct ovsdb_jsonrpc_session *session;
 struct ovsdb_jsonrpc_monitor {
     struct ovsdb_replica replica;
     struct ovsdb_jsonrpc_session *session;
+    struct ovsdb *db;
     struct hmap_node node;      /* In ovsdb_jsonrpc_session's "monitors". */
 
     struct json *monitor_id;
     struct hmap_node node;      /* In ovsdb_jsonrpc_session's "monitors". */
 
     struct json *monitor_id;
@@ -1113,7 +1139,7 @@ ovsdb_jsonrpc_parse_monitor_request(struct ovsdb_jsonrpc_monitor_table *mt,
 }
 
 static struct json *
 }
 
 static struct json *
-ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s,
+ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db,
                              struct json *params)
 {
     struct ovsdb_jsonrpc_monitor *m = NULL;
                              struct json *params)
 {
     struct ovsdb_jsonrpc_monitor *m = NULL;
@@ -1141,8 +1167,9 @@ ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s,
 
     m = xzalloc(sizeof *m);
     ovsdb_replica_init(&m->replica, &ovsdb_jsonrpc_replica_class);
 
     m = xzalloc(sizeof *m);
     ovsdb_replica_init(&m->replica, &ovsdb_jsonrpc_replica_class);
-    ovsdb_add_replica(s->remote->server->up.db, &m->replica);
+    ovsdb_add_replica(db, &m->replica);
     m->session = s;
     m->session = s;
+    m->db = db;
     hmap_insert(&s->monitors, &m->node, json_hash(monitor_id, 0));
     m->monitor_id = json_clone(monitor_id);
     shash_init(&m->tables);
     hmap_insert(&s->monitors, &m->node, json_hash(monitor_id, 0));
     m->monitor_id = json_clone(monitor_id);
     shash_init(&m->tables);
@@ -1154,7 +1181,7 @@ ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s,
         const struct json *mr_value;
         size_t i;
 
         const struct json *mr_value;
         size_t i;
 
-        table = ovsdb_get_table(s->remote->server->up.db, node->name);
+        table = ovsdb_get_table(m->db, node->name);
         if (!table) {
             error = ovsdb_syntax_error(NULL, NULL,
                                        "no table named %s", node->name);
         if (!table) {
             error = ovsdb_syntax_error(NULL, NULL,
                                        "no table named %s", node->name);
@@ -1203,7 +1230,7 @@ ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s,
 
 error:
     if (m) {
 
 error:
     if (m) {
-        ovsdb_remove_replica(s->remote->server->up.db, &m->replica);
+        ovsdb_remove_replica(m->db, &m->replica);
     }
 
     json = ovsdb_error_to_json(error);
     }
 
     json = ovsdb_error_to_json(error);
@@ -1227,7 +1254,7 @@ ovsdb_jsonrpc_monitor_cancel(struct ovsdb_jsonrpc_session *s,
             return jsonrpc_create_error(json_string_create("unknown monitor"),
                                         request_id);
         } else {
             return jsonrpc_create_error(json_string_create("unknown monitor"),
                                         request_id);
         } else {
-            ovsdb_remove_replica(s->remote->server->up.db, &m->replica);
+            ovsdb_remove_replica(m->db, &m->replica);
             return jsonrpc_create_reply(json_object_create(), request_id);
         }
     }
             return jsonrpc_create_reply(json_object_create(), request_id);
         }
     }
@@ -1239,7 +1266,7 @@ ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *s)
     struct ovsdb_jsonrpc_monitor *m, *next;
 
     HMAP_FOR_EACH_SAFE (m, next, node, &s->monitors) {
     struct ovsdb_jsonrpc_monitor *m, *next;
 
     HMAP_FOR_EACH_SAFE (m, next, node, &s->monitors) {
-        ovsdb_remove_replica(s->remote->server->up.db, &m->replica);
+        ovsdb_remove_replica(m->db, &m->replica);
     }
 }
 
     }
 }
 
index 2dc0c780b3822454fd636588621ff894dc1201e2..bf2a2fcae73ae7ecc5886509b7ab5866257b01b2 100644 (file)
@@ -22,7 +22,9 @@ struct ovsdb;
 struct shash;
 struct simap;
 
 struct shash;
 struct simap;
 
-struct ovsdb_jsonrpc_server *ovsdb_jsonrpc_server_create(struct ovsdb *);
+struct ovsdb_jsonrpc_server *ovsdb_jsonrpc_server_create(void);
+bool ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *,
+                                 struct ovsdb *);
 void ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *);
 
 /* Options for a remote. */
 void ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *);
 
 /* Options for a remote. */
index 1f5be03a8e6d09c513c26bcee66392d021aa403b..b4b6eb62900e46ef5033646c59bc457ba29d6608 100644 (file)
@@ -10,7 +10,7 @@ ovsdb\-server \- Open vSwitch database server
 .
 .SH SYNOPSIS
 \fBovsdb\-server\fR
 .
 .SH SYNOPSIS
 \fBovsdb\-server\fR
-[\fIdatabase\fR]
+[\fIdatabase\fR]\&...
 [\fB\-\-remote=\fIremote\fR]\&...
 [\fB\-\-run=\fIcommand\fR]
 .so lib/daemon-syn.man
 [\fB\-\-remote=\fIremote\fR]\&...
 [\fB\-\-run=\fIcommand\fR]
 .so lib/daemon-syn.man
@@ -21,13 +21,13 @@ ovsdb\-server \- Open vSwitch database server
 .so lib/common-syn.man
 .
 .SH DESCRIPTION
 .so lib/common-syn.man
 .
 .SH DESCRIPTION
-The \fBovsdb\-server\fR program provides RPC interfaces to an Open
-vSwitch database (OVSDB).  It supports JSON-RPC client connections
-over active or passive TCP/IP or Unix domain sockets.
+The \fBovsdb\-server\fR program provides RPC interfaces to one or more
+Open vSwitch databases (OVSDBs).  It supports JSON-RPC client
+connections over active or passive TCP/IP or Unix domain sockets.
 .PP
 .PP
-The OVSDB file may be specified on the command line as \fIdatabase\fR.
-The default is \fB@DBDIR@/conf.db\fR.  The database
-file must already have been created and initialized using, for
+Each OVSDB file may be specified on the command line as \fIdatabase\fR.
+If none is specified, the default is \fB@DBDIR@/conf.db\fR.  The database
+files must already have been created and initialized using, for
 example, \fBovsdb\-tool create\fR.
 .
 .SH OPTIONS
 example, \fBovsdb\-tool create\fR.
 .
 .SH OPTIONS
@@ -40,11 +40,12 @@ Adds \fIremote\fR as a connection method used by \fBovsdb\-server\fR.
 .so ovsdb/remote-passive.man
 .so ovsdb/remote-active.man
 .
 .so ovsdb/remote-passive.man
 .so ovsdb/remote-active.man
 .
-.IP "\fBdb:\fItable\fB,\fIcolumn\fR"
+.IP "\fBdb:\fR[\fIdb\fB,\fR]\fItable\fB,\fIcolumn\fR"
 Reads additional connection methods from \fIcolumn\fR in all of the
 Reads additional connection methods from \fIcolumn\fR in all of the
-rows in \fItable\fR.  As the contents of \fIcolumn\fR changes,
-\fBovsdb\-server\fR also adds and drops connection methods
-accordingly.
+rows in \fItable\fR within \fIdb\fR.  (If \fBovsdb\-server\fR is
+providing access to only one database, then \fIdb\fR is optional.)  As
+the contents of \fIcolumn\fR changes, \fBovsdb\-server\fR also adds
+and drops connection methods accordingly.
 .IP
 If \fIcolumn\fR's type is string or set of strings, then the
 connection methods are taken directly from the column.  The connection
 .IP
 If \fIcolumn\fR's type is string or set of strings, then the
 connection methods are taken directly from the column.  The connection
@@ -61,7 +62,7 @@ is mandatory: if it is missing or empty then no connection method can
 be configured.
 .IP "\fBmax_backoff\fR (integer)"
 Maximum number of milliseconds to wait between connection attempts.
 be configured.
 .IP "\fBmax_backoff\fR (integer)"
 Maximum number of milliseconds to wait between connection attempts.
-.IP "\fBinactivity_probe\fR (integer)
+.IP "\fBinactivity_probe\fR (integer)"
 Maximum number of milliseconds of idle time on connection to
 client before sending an inactivity probe message.
 .RE
 Maximum number of milliseconds of idle time on connection to
 client before sending an inactivity probe message.
 .RE
@@ -111,9 +112,10 @@ described below.
 These commands are specific to \fBovsdb\-server\fR.
 .IP "\fBexit\fR"
 Causes \fBovsdb\-server\fR to gracefully terminate.
 These commands are specific to \fBovsdb\-server\fR.
 .IP "\fBexit\fR"
 Causes \fBovsdb\-server\fR to gracefully terminate.
-.IP "\fBovsdb\-server/compact\fR"
-Compacts the database in-place.  The database is also automatically
-compacted occasionally.
+.IP "\fBovsdb\-server/compact\fR [\fIdb\fR]\&..."
+Compacts each database \fIdb\fR in-place.  If no \fIdb\fR is
+specified, compacts every database in-place.  Databases are also
+automatically compacted occasionally.
 .
 .IP "\fBovsdb\-server/reconnect\fR"
 Makes \fBovsdb\-server\fR drop all of the JSON\-RPC
 .
 .IP "\fBovsdb\-server/reconnect\fR"
 Makes \fBovsdb\-server\fR drop all of the JSON\-RPC
index 0baf9fcf15ebbceee1f0cc10f110d28b5f0d87e4..69548c202f5db13b30e22004bba4bf18a5a8856f 100644 (file)
@@ -26,6 +26,7 @@
 #include "daemon.h"
 #include "dirs.h"
 #include "dummy.h"
 #include "daemon.h"
 #include "dirs.h"
 #include "dummy.h"
+#include "dynamic-string.h"
 #include "file.h"
 #include "hash.h"
 #include "json.h"
 #include "file.h"
 #include "hash.h"
 #include "json.h"
 
 VLOG_DEFINE_THIS_MODULE(ovsdb_server);
 
 
 VLOG_DEFINE_THIS_MODULE(ovsdb_server);
 
+struct db {
+    /* Initialized in main(). */
+    char *filename;
+    struct ovsdb_file *file;
+    struct ovsdb *db;
+
+    /* Only used by update_remote_status(). */
+    struct ovsdb_txn *txn;
+};
+
 /* SSL configuration. */
 static char *private_key_file;
 static char *certificate_file;
 /* SSL configuration. */
 static char *private_key_file;
 static char *certificate_file;
@@ -66,17 +77,18 @@ static unixctl_cb_func ovsdb_server_exit;
 static unixctl_cb_func ovsdb_server_compact;
 static unixctl_cb_func ovsdb_server_reconnect;
 
 static unixctl_cb_func ovsdb_server_compact;
 static unixctl_cb_func ovsdb_server_reconnect;
 
-static void parse_options(int argc, char *argv[], char **file_namep,
+static void parse_options(int *argc, char **argvp[],
                           struct sset *remotes, char **unixctl_pathp,
                           char **run_command);
 static void usage(void) NO_RETURN;
 
 static void reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
                           struct sset *remotes, char **unixctl_pathp,
                           char **run_command);
 static void usage(void) NO_RETURN;
 
 static void reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
-                                const struct ovsdb *db, struct sset *remotes);
+                                const struct db dbs[], size_t n_dbs,
+                                struct sset *remotes);
 
 static void update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc,
                                  const struct sset *remotes,
 
 static void update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc,
                                  const struct sset *remotes,
-                                 struct ovsdb *db);
+                                 struct db dbs[], size_t n_dbs);
 
 int
 main(int argc, char *argv[])
 
 int
 main(int argc, char *argv[])
@@ -86,34 +98,53 @@ main(int argc, char *argv[])
     struct unixctl_server *unixctl;
     struct ovsdb_jsonrpc_server *jsonrpc;
     struct sset remotes;
     struct unixctl_server *unixctl;
     struct ovsdb_jsonrpc_server *jsonrpc;
     struct sset remotes;
-    struct ovsdb_error *error;
-    struct ovsdb_file *file;
-    struct ovsdb *db;
     struct process *run_process;
     struct process *run_process;
-    char *file_name;
     bool exiting;
     int retval;
     long long int status_timer = LLONG_MIN;
 
     bool exiting;
     int retval;
     long long int status_timer = LLONG_MIN;
 
+    struct db *dbs;
+    int n_dbs;
+    int i;
+
     proctitle_init(argc, argv);
     set_program_name(argv[0]);
     stress_init_command();
     signal(SIGPIPE, SIG_IGN);
     process_init();
 
     proctitle_init(argc, argv);
     set_program_name(argv[0]);
     stress_init_command();
     signal(SIGPIPE, SIG_IGN);
     process_init();
 
-    parse_options(argc, argv, &file_name, &remotes, &unixctl_path,
-                  &run_command);
+    parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command);
 
     daemonize_start();
 
 
     daemonize_start();
 
-    error = ovsdb_file_open(file_name, false, &db, &file);
-    if (error) {
-        ovs_fatal(0, "%s", ovsdb_error_to_string(error));
+    n_dbs = MAX(1, argc);
+    dbs = xcalloc(n_dbs + 1, sizeof *dbs);
+    if (argc > 0) {
+        for (i = 0; i < argc; i++) {
+            dbs[i].filename = argv[i];
+        }
+    } else {
+        dbs[0].filename = xasprintf("%s/conf.db", ovs_dbdir());
     }
     }
-    free(file_name);
 
 
-    jsonrpc = ovsdb_jsonrpc_server_create(db);
-    reconfigure_from_db(jsonrpc, db, &remotes);
+    for (i = 0; i < n_dbs; i++) {
+        struct ovsdb_error *error;
+
+        error = ovsdb_file_open(dbs[i].filename, false,
+                                &dbs[i].db, &dbs[i].file);
+        if (error) {
+            ovs_fatal(0, "%s", ovsdb_error_to_string(error));
+        }
+    }
+
+    jsonrpc = ovsdb_jsonrpc_server_create();
+    for (i = 0; i < n_dbs; i++) {
+        if (!ovsdb_jsonrpc_server_add_db(jsonrpc, dbs[i].db)) {
+            ovs_fatal(0, "%s: duplicate database name",
+                      dbs[i].db->schema->name);
+        }
+    }
+    reconfigure_from_db(jsonrpc, dbs, n_dbs, &remotes);
 
     retval = unixctl_server_create(unixctl_path, &unixctl);
     if (retval) {
 
     retval = unixctl_server_create(unixctl_path, &unixctl);
     if (retval) {
@@ -146,28 +177,35 @@ main(int argc, char *argv[])
     }
 
     unixctl_command_register("exit", "", 0, 0, ovsdb_server_exit, &exiting);
     }
 
     unixctl_command_register("exit", "", 0, 0, ovsdb_server_exit, &exiting);
-    unixctl_command_register("ovsdb-server/compact", "", 0, 0,
-                             ovsdb_server_compact, file);
+    unixctl_command_register("ovsdb-server/compact", "", 0, 1,
+                             ovsdb_server_compact, dbs);
     unixctl_command_register("ovsdb-server/reconnect", "", 0, 0,
                              ovsdb_server_reconnect, jsonrpc);
 
     exiting = false;
     while (!exiting) {
     unixctl_command_register("ovsdb-server/reconnect", "", 0, 0,
                              ovsdb_server_reconnect, jsonrpc);
 
     exiting = false;
     while (!exiting) {
+        int i;
+
         memory_run();
         if (memory_should_report()) {
             struct simap usage;
 
             simap_init(&usage);
             ovsdb_jsonrpc_server_get_memory_usage(jsonrpc, &usage);
         memory_run();
         if (memory_should_report()) {
             struct simap usage;
 
             simap_init(&usage);
             ovsdb_jsonrpc_server_get_memory_usage(jsonrpc, &usage);
-            ovsdb_get_memory_usage(db, &usage);
+            for (i = 0; i < n_dbs; i++) {
+                ovsdb_get_memory_usage(dbs[i].db, &usage);
+            }
             memory_report(&usage);
             simap_destroy(&usage);
         }
 
             memory_report(&usage);
             simap_destroy(&usage);
         }
 
-        reconfigure_from_db(jsonrpc, db, &remotes);
+        reconfigure_from_db(jsonrpc, dbs, n_dbs, &remotes);
         ovsdb_jsonrpc_server_run(jsonrpc);
         unixctl_server_run(unixctl);
         ovsdb_jsonrpc_server_run(jsonrpc);
         unixctl_server_run(unixctl);
-        ovsdb_trigger_run(db, time_msec());
+
+        for (i = 0; i < n_dbs; i++) {
+            ovsdb_trigger_run(dbs[i].db, time_msec());
+        }
         if (run_process && process_exited(run_process)) {
             exiting = true;
         }
         if (run_process && process_exited(run_process)) {
             exiting = true;
         }
@@ -175,13 +213,15 @@ main(int argc, char *argv[])
         /* update Manager status(es) every 5 seconds */
         if (time_msec() >= status_timer) {
             status_timer = time_msec() + 5000;
         /* update Manager status(es) every 5 seconds */
         if (time_msec() >= status_timer) {
             status_timer = time_msec() + 5000;
-            update_remote_status(jsonrpc, &remotes, db);
+            update_remote_status(jsonrpc, &remotes, dbs, n_dbs);
         }
 
         memory_wait();
         ovsdb_jsonrpc_server_wait(jsonrpc);
         unixctl_server_wait(unixctl);
         }
 
         memory_wait();
         ovsdb_jsonrpc_server_wait(jsonrpc);
         unixctl_server_wait(unixctl);
-        ovsdb_trigger_wait(db, time_msec());
+        for (i = 0; i < n_dbs; i++) {
+            ovsdb_trigger_wait(dbs[i].db, time_msec());
+        }
         if (run_process) {
             process_wait(run_process);
         }
         if (run_process) {
             process_wait(run_process);
         }
@@ -192,7 +232,9 @@ main(int argc, char *argv[])
         poll_block();
     }
     ovsdb_jsonrpc_server_destroy(jsonrpc);
         poll_block();
     }
     ovsdb_jsonrpc_server_destroy(jsonrpc);
-    ovsdb_destroy(db);
+    for (i = 0; i < n_dbs; i++) {
+        ovsdb_destroy(dbs[i].db);
+    }
     sset_destroy(&remotes);
     unixctl_server_destroy(unixctl);
 
     sset_destroy(&remotes);
     unixctl_server_destroy(unixctl);
 
@@ -207,26 +249,64 @@ main(int argc, char *argv[])
     return 0;
 }
 
     return 0;
 }
 
+static const struct db *
+find_db(const struct db dbs[], size_t n_dbs, const char *db_name)
+{
+    size_t i;
+
+    for (i = 0; i < n_dbs; i++) {
+        if (!strcmp(dbs[i].db->schema->name, db_name)) {
+            return &dbs[i];
+        }
+    }
+
+    return NULL;
+}
+
 static void
 static void
-parse_db_column(const struct ovsdb *db,
+parse_db_column(const struct db dbs[], size_t n_dbs,
                 const char *name_,
                 const char *name_,
+                const struct db **dbp,
                 const struct ovsdb_table **tablep,
                 const struct ovsdb_column **columnp)
 {
                 const struct ovsdb_table **tablep,
                 const struct ovsdb_column **columnp)
 {
-    char *name, *table_name, *column_name;
+    const char *table_name, *column_name;
     const struct ovsdb_column *column;
     const struct ovsdb_table *table;
     const struct ovsdb_column *column;
     const struct ovsdb_table *table;
+    const char *tokens[3];
     char *save_ptr = NULL;
     char *save_ptr = NULL;
+    const struct db *db;
+    char *name;
 
     name = xstrdup(name_);
     strtok_r(name, ":", &save_ptr); /* "db:" */
 
     name = xstrdup(name_);
     strtok_r(name, ":", &save_ptr); /* "db:" */
-    table_name = strtok_r(NULL, ",", &save_ptr);
-    column_name = strtok_r(NULL, ",", &save_ptr);
-    if (!table_name || !column_name) {
+    tokens[0] = strtok_r(NULL, ",", &save_ptr);
+    tokens[1] = strtok_r(NULL, ",", &save_ptr);
+    tokens[2] = strtok_r(NULL, ",", &save_ptr);
+    if (!tokens[0] || !tokens[1]) {
         ovs_fatal(0, "\"%s\": invalid syntax", name_);
     }
         ovs_fatal(0, "\"%s\": invalid syntax", name_);
     }
+    if (tokens[2]) {
+        const char *db_name = tokens[0];
+        table_name = tokens[1];
+        column_name = tokens[2];
+
+        db = find_db(dbs, n_dbs, tokens[0]);
+        if (!db) {
+            ovs_fatal(0, "\"%s\": no database named %s", name_, db_name);
+        }
+    } else {
+        if (n_dbs > 1) {
+            ovs_fatal(0, "\"%s\": database name must be specified (because "
+                      "multiple databases are configured)", name_);
+        }
+
+        table_name = tokens[0];
+        column_name = tokens[1];
+        db = &dbs[0];
+    }
 
 
-    table = ovsdb_get_table(db, table_name);
+    table = ovsdb_get_table(db->db, table_name);
     if (!table) {
         ovs_fatal(0, "\"%s\": no table named %s", name_, table_name);
     }
     if (!table) {
         ovs_fatal(0, "\"%s\": no table named %s", name_, table_name);
     }
@@ -238,20 +318,23 @@ parse_db_column(const struct ovsdb *db,
     }
     free(name);
 
     }
     free(name);
 
+    *dbp = db;
     *columnp = column;
     *tablep = table;
 }
 
 static void
     *columnp = column;
     *tablep = table;
 }
 
 static void
-parse_db_string_column(const struct ovsdb *db,
+parse_db_string_column(const struct db dbs[], size_t n_dbs,
                        const char *name,
                        const char *name,
+                       const struct db **dbp,
                        const struct ovsdb_table **tablep,
                        const struct ovsdb_column **columnp)
 {
     const struct ovsdb_column *column;
     const struct ovsdb_table *table;
                        const struct ovsdb_table **tablep,
                        const struct ovsdb_column **columnp)
 {
     const struct ovsdb_column *column;
     const struct ovsdb_table *table;
+    const struct db *db;
 
 
-    parse_db_column(db, name, &table, &column);
+    parse_db_column(dbs, n_dbs, name, &db, &table, &column);
 
     if (column->type.key.type != OVSDB_TYPE_STRING
         || column->type.value.type != OVSDB_TYPE_VOID) {
 
     if (column->type.key.type != OVSDB_TYPE_STRING
         || column->type.value.type != OVSDB_TYPE_VOID) {
@@ -260,12 +343,13 @@ parse_db_string_column(const struct ovsdb *db,
                   name, table->schema->name, column->name);
     }
 
                   name, table->schema->name, column->name);
     }
 
+    *dbp = db;
     *columnp = column;
     *tablep = table;
 }
 
 static OVS_UNUSED const char *
     *columnp = column;
     *tablep = table;
 }
 
 static OVS_UNUSED const char *
-query_db_string(const struct ovsdb *db, const char *name)
+query_db_string(const struct db dbs[], size_t n_dbs, const char *name)
 {
     if (!name || strncmp(name, "db:", 3)) {
         return name;
 {
     if (!name || strncmp(name, "db:", 3)) {
         return name;
@@ -273,8 +357,9 @@ query_db_string(const struct ovsdb *db, const char *name)
         const struct ovsdb_column *column;
         const struct ovsdb_table *table;
         const struct ovsdb_row *row;
         const struct ovsdb_column *column;
         const struct ovsdb_table *table;
         const struct ovsdb_row *row;
+        const struct db *db;
 
 
-        parse_db_string_column(db, name, &table, &column);
+        parse_db_string_column(dbs, n_dbs, name, &db, &table, &column);
 
         HMAP_FOR_EACH (row, hmap_node, &table->rows) {
             const struct ovsdb_datum *datum;
 
         HMAP_FOR_EACH (row, hmap_node, &table->rows) {
             const struct ovsdb_datum *datum;
@@ -493,14 +578,15 @@ add_manager_options(struct shash *remotes, const struct ovsdb_row *row)
 }
 
 static void
 }
 
 static void
-query_db_remotes(const char *name, const struct ovsdb *db,
+query_db_remotes(const char *name, const struct db dbs[], size_t n_dbs,
                  struct shash *remotes)
 {
     const struct ovsdb_column *column;
     const struct ovsdb_table *table;
     const struct ovsdb_row *row;
                  struct shash *remotes)
 {
     const struct ovsdb_column *column;
     const struct ovsdb_table *table;
     const struct ovsdb_row *row;
+    const struct db *db;
 
 
-    parse_db_column(db, name, &table, &column);
+    parse_db_column(dbs, n_dbs, name, &db, &table, &column);
 
     if (column->type.key.type == OVSDB_TYPE_STRING
         && column->type.value.type == OVSDB_TYPE_VOID) {
 
     if (column->type.key.type == OVSDB_TYPE_STRING
         && column->type.value.type == OVSDB_TYPE_VOID) {
@@ -594,19 +680,20 @@ update_remote_row(const struct ovsdb_row *row, struct ovsdb_txn *txn,
 }
 
 static void
 }
 
 static void
-update_remote_rows(const struct ovsdb *db, struct ovsdb_txn *txn,
+update_remote_rows(const struct db dbs[], size_t n_dbs,
                    const char *remote_name,
                    const struct ovsdb_jsonrpc_server *jsonrpc)
 {
     const struct ovsdb_table *table, *ref_table;
     const struct ovsdb_column *column;
     const struct ovsdb_row *row;
                    const char *remote_name,
                    const struct ovsdb_jsonrpc_server *jsonrpc)
 {
     const struct ovsdb_table *table, *ref_table;
     const struct ovsdb_column *column;
     const struct ovsdb_row *row;
+    const struct db *db;
 
     if (strncmp("db:", remote_name, 3)) {
         return;
     }
 
 
     if (strncmp("db:", remote_name, 3)) {
         return;
     }
 
-    parse_db_column(db, remote_name, &table, &column);
+    parse_db_column(dbs, n_dbs, remote_name, &db, &table, &column);
 
     if (column->type.key.type != OVSDB_TYPE_UUID
         || !column->type.key.u.uuid.refTable
 
     if (column->type.key.type != OVSDB_TYPE_UUID
         || !column->type.key.u.uuid.refTable
@@ -626,7 +713,7 @@ update_remote_rows(const struct ovsdb *db, struct ovsdb_txn *txn,
 
             ref_row = ovsdb_table_get_row(ref_table, &datum->keys[i].uuid);
             if (ref_row) {
 
             ref_row = ovsdb_table_get_row(ref_table, &datum->keys[i].uuid);
             if (ref_row) {
-                update_remote_row(ref_row, txn, jsonrpc);
+                update_remote_row(ref_row, db->txn, jsonrpc);
             }
         }
     }
             }
         }
     }
@@ -634,32 +721,36 @@ update_remote_rows(const struct ovsdb *db, struct ovsdb_txn *txn,
 
 static void
 update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc,
 
 static void
 update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc,
-                     const struct sset *remotes, struct ovsdb *db)
+                     const struct sset *remotes,
+                     struct db dbs[], size_t n_dbs)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-    struct ovsdb_txn *txn;
-    const bool durable_txn = false;
-    struct ovsdb_error *error;
     const char *remote;
     const char *remote;
+    size_t i;
 
 
-    txn = ovsdb_txn_create(db);
+    for (i = 0; i < n_dbs; i++) {
+        dbs[i].txn = ovsdb_txn_create(dbs[i].db);
+    }
 
     /* Iterate over --remote arguments given on command line. */
     SSET_FOR_EACH (remote, remotes) {
 
     /* Iterate over --remote arguments given on command line. */
     SSET_FOR_EACH (remote, remotes) {
-        update_remote_rows(db, txn, remote, jsonrpc);
+        update_remote_rows(dbs, n_dbs, remote, jsonrpc);
     }
 
     }
 
-    error = ovsdb_txn_commit(txn, durable_txn);
-    if (error) {
-        VLOG_ERR_RL(&rl, "Failed to update remote status: %s",
-                    ovsdb_error_to_string(error));
+    for (i = 0; i < n_dbs; i++) {
+        struct ovsdb_error *error = ovsdb_txn_commit(dbs[i].txn, false);
+        if (error) {
+            VLOG_ERR_RL(&rl, "Failed to update remote status: %s",
+                        ovsdb_error_to_string(error));
+            ovsdb_error_destroy(error);
+        }
     }
 }
 
 /* Reconfigures ovsdb-server based on information in the database. */
 static void
 reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
     }
 }
 
 /* Reconfigures ovsdb-server based on information in the database. */
 static void
 reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
-                    const struct ovsdb *db, struct sset *remotes)
+                    const struct db dbs[], size_t n_dbs, struct sset *remotes)
 {
     struct shash resolved_remotes;
     const char *name;
 {
     struct shash resolved_remotes;
     const char *name;
@@ -668,7 +759,7 @@ reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
     shash_init(&resolved_remotes);
     SSET_FOR_EACH (name, remotes) {
         if (!strncmp(name, "db:", 3)) {
     shash_init(&resolved_remotes);
     SSET_FOR_EACH (name, remotes) {
         if (!strncmp(name, "db:", 3)) {
-            query_db_remotes(name, db, &resolved_remotes);
+            query_db_remotes(name, dbs, n_dbs, &resolved_remotes);
         } else {
             add_remote(&resolved_remotes, name);
         }
         } else {
             add_remote(&resolved_remotes, name);
         }
@@ -677,9 +768,9 @@ reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
     shash_destroy_free_data(&resolved_remotes);
 
     /* Configure SSL. */
     shash_destroy_free_data(&resolved_remotes);
 
     /* Configure SSL. */
-    stream_ssl_set_key_and_cert(query_db_string(db, private_key_file),
-                                query_db_string(db, certificate_file));
-    stream_ssl_set_ca_cert_file(query_db_string(db, ca_cert_file),
+    stream_ssl_set_key_and_cert(query_db_string(dbs, n_dbs, private_key_file),
+                                query_db_string(dbs, n_dbs, certificate_file));
+    stream_ssl_set_ca_cert_file(query_db_string(dbs, n_dbs, ca_cert_file),
                                 bootstrap_ca_cert);
 }
 
                                 bootstrap_ca_cert);
 }
 
@@ -694,22 +785,42 @@ ovsdb_server_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
 }
 
 static void
 }
 
 static void
-ovsdb_server_compact(struct unixctl_conn *conn, int argc OVS_UNUSED,
-                     const char *argv[] OVS_UNUSED, void *file_)
+ovsdb_server_compact(struct unixctl_conn *conn, int argc,
+                     const char *argv[], void *dbs_)
 {
 {
-    struct ovsdb_file *file = file_;
-    struct ovsdb_error *error;
+    struct db *dbs = dbs_;
+    struct ds reply;
+    struct db *db;
+    int n = 0;
 
 
-    VLOG_INFO("compacting database by user request");
-    error = ovsdb_file_compact(file);
-    if (!error) {
-        unixctl_command_reply(conn, NULL);
+    ds_init(&reply);
+    for (db = dbs; db->filename != NULL; db++) {
+        const char *name = db->db->schema->name;
+
+        if (argc < 2 || !strcmp(argv[1], name)) {
+            struct ovsdb_error *error;
+
+            VLOG_INFO("compacting %s database by user request", name);
+
+            error = ovsdb_file_compact(db->file);
+            if (error) {
+                char *s = ovsdb_error_to_string(error);
+                ds_put_format(&reply, "%s\n", s);
+                free(s);
+            }
+
+            n++;
+        }
+    }
+
+    if (!n) {
+        unixctl_command_reply_error(conn, "no database by that name");
+    } else if (reply.length) {
+        unixctl_command_reply_error(conn, ds_cstr(&reply));
     } else {
     } else {
-        char *s = ovsdb_error_to_string(error);
-        ovsdb_error_destroy(error);
-        unixctl_command_reply_error(conn, s);
-        free(s);
+        unixctl_command_reply(conn, NULL);
     }
     }
+    ds_destroy(&reply);
 }
 
 /* "ovsdb-server/reconnect": makes ovsdb-server drop all of its JSON-RPC
 }
 
 /* "ovsdb-server/reconnect": makes ovsdb-server drop all of its JSON-RPC
@@ -725,9 +836,8 @@ ovsdb_server_reconnect(struct unixctl_conn *conn, int argc OVS_UNUSED,
 }
 
 static void
 }
 
 static void
-parse_options(int argc, char *argv[], char **file_namep,
-              struct sset *remotes, char **unixctl_pathp,
-              char **run_command)
+parse_options(int *argcp, char **argvp[],
+              struct sset *remotes, char **unixctl_pathp, char **run_command)
 {
     enum {
         OPT_REMOTE = UCHAR_MAX + 1,
 {
     enum {
         OPT_REMOTE = UCHAR_MAX + 1,
@@ -756,6 +866,8 @@ parse_options(int argc, char *argv[], char **file_namep,
         {NULL, 0, NULL, 0},
     };
     char *short_options = long_options_to_short_options(long_options);
         {NULL, 0, NULL, 0},
     };
     char *short_options = long_options_to_short_options(long_options);
+    int argc = *argcp;
+    char **argv = *argvp;
 
     sset_init(remotes);
     for (;;) {
 
     sset_init(remotes);
     for (;;) {
@@ -821,31 +933,18 @@ parse_options(int argc, char *argv[], char **file_namep,
     }
     free(short_options);
 
     }
     free(short_options);
 
-    argc -= optind;
-    argv += optind;
-
-    switch (argc) {
-    case 0:
-        *file_namep = xasprintf("%s/conf.db", ovs_dbdir());
-        break;
-
-    case 1:
-        *file_namep = xstrdup(argv[0]);
-        break;
-
-    default:
-        ovs_fatal(0, "database file is only non-option argument; "
-                "use --help for usage");
-    }
+    *argcp -= optind;
+    *argvp += optind;
 }
 
 static void
 usage(void)
 {
     printf("%s: Open vSwitch database server\n"
 }
 
 static void
 usage(void)
 {
     printf("%s: Open vSwitch database server\n"
-           "usage: %s [OPTIONS] DATABASE\n"
-           "where DATABASE is a database file in ovsdb format.\n",
-           program_name, program_name);
+           "usage: %s [OPTIONS] [DATABASE...]\n"
+           "where each DATABASE is a database file in ovsdb format.\n"
+           "The default DATABASE, if none is given, is\n%s/conf.db.\n",
+           program_name, program_name, ovs_dbdir());
     printf("\nJSON-RPC options (may be specified any number of times):\n"
            "  --remote=REMOTE         connect or listen to REMOTE\n");
     stream_usage("JSON-RPC", true, true, true);
     printf("\nJSON-RPC options (may be specified any number of times):\n"
            "  --remote=REMOTE         connect or listen to REMOTE\n");
     stream_usage("JSON-RPC", true, true, true);
index 7cd42638ca4f8d1bcfa365f4b76b99ad100f8e00..ac2aa29dfb78f85e9b4fe7028039e2f0da3fcee1 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 Nicira, Inc.
+/* Copyright (c) 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <assert.h>
 
 #include "hash.h"
 #include <assert.h>
 
 #include "hash.h"
+#include "ovsdb.h"
 
 
-/* Initializes 'session' as a session that operates on 'db'. */
+/* Initializes 'session' as a session within 'server'. */
 void
 void
-ovsdb_session_init(struct ovsdb_session *session, struct ovsdb *db)
+ovsdb_session_init(struct ovsdb_session *session, struct ovsdb_server *server)
 {
 {
-    session->db = db;
+    session->server = server;
     list_init(&session->completions);
     hmap_init(&session->waiters);
 }
     list_init(&session->completions);
     hmap_init(&session->waiters);
 }
@@ -113,18 +114,31 @@ ovsdb_lock_waiter_is_owner(const struct ovsdb_lock_waiter *waiter)
     return waiter->lock && waiter == ovsdb_lock_get_owner(waiter->lock);
 }
 
     return waiter->lock && waiter == ovsdb_lock_get_owner(waiter->lock);
 }
 
-/* Initializes 'server' as a server that operates on 'db'. */
+/* Initializes 'server'.
+ *
+ * The caller must call ovsdb_server_add_db() for each database to which
+ * 'server' should provide access. */
 void
 void
-ovsdb_server_init(struct ovsdb_server *server, struct ovsdb *db)
+ovsdb_server_init(struct ovsdb_server *server)
 {
 {
-    server->db = db;
+    shash_init(&server->dbs);
     hmap_init(&server->locks);
 }
 
     hmap_init(&server->locks);
 }
 
+/* Adds 'db' to the set of databases served out by 'server'.  Returns true if
+ * successful, false if 'db''s name is the same as some database already in
+ * 'server'. */
+bool
+ovsdb_server_add_db(struct ovsdb_server *server, struct ovsdb *db)
+{
+    return shash_add_once(&server->dbs, db->schema->name, db);
+}
+
 /* Destroys 'server'. */
 void
 ovsdb_server_destroy(struct ovsdb_server *server)
 {
 /* Destroys 'server'. */
 void
 ovsdb_server_destroy(struct ovsdb_server *server)
 {
+    shash_destroy(&server->dbs);
     hmap_destroy(&server->locks);
 }
 
     hmap_destroy(&server->locks);
 }
 
index e0738500f7e14c6cc3b2e9fdec3fe76eb8e7f7e9..561f01ebf3b5b00b99c8c876305e374af284db88 100644 (file)
 
 #include "hmap.h"
 #include "list.h"
 
 #include "hmap.h"
 #include "list.h"
+#include "shash.h"
+
+struct ovsdb;
+struct ovsdb_server;
 
 /* Abstract representation of an OVSDB client connection, not tied to any
  * particular network protocol.  Protocol implementations
  * (e.g. jsonrpc-server.c) embed this in a larger data structure.  */
 struct ovsdb_session {
 
 /* Abstract representation of an OVSDB client connection, not tied to any
  * particular network protocol.  Protocol implementations
  * (e.g. jsonrpc-server.c) embed this in a larger data structure.  */
 struct ovsdb_session {
-    struct ovsdb *db;
+    struct ovsdb_server *server;
     struct list completions;    /* Completed triggers. */
     struct hmap waiters;        /* "ovsdb_lock_waiter *"s by lock name. */
 };
 
     struct list completions;    /* Completed triggers. */
     struct hmap waiters;        /* "ovsdb_lock_waiter *"s by lock name. */
 };
 
-void ovsdb_session_init(struct ovsdb_session *, struct ovsdb *);
+void ovsdb_session_init(struct ovsdb_session *, struct ovsdb_server *);
 void ovsdb_session_destroy(struct ovsdb_session *);
 
 struct ovsdb_lock_waiter *ovsdb_session_get_lock_waiter(
 void ovsdb_session_destroy(struct ovsdb_session *);
 
 struct ovsdb_lock_waiter *ovsdb_session_get_lock_waiter(
@@ -73,11 +77,12 @@ bool ovsdb_lock_waiter_is_owner(const struct ovsdb_lock_waiter *);
  * network protocol.  Protocol implementations (e.g. jsonrpc-server.c) embed
  * this in a larger data structure.  */
 struct ovsdb_server {
  * network protocol.  Protocol implementations (e.g. jsonrpc-server.c) embed
  * this in a larger data structure.  */
 struct ovsdb_server {
-    struct ovsdb *db;
+    struct shash dbs;      /* Maps from a db name to a "struct ovsdb *". */
     struct hmap locks;     /* Contains "struct ovsdb_lock"s indexed by name. */
 };
 
     struct hmap locks;     /* Contains "struct ovsdb_lock"s indexed by name. */
 };
 
-void ovsdb_server_init(struct ovsdb_server *, struct ovsdb *);
+void ovsdb_server_init(struct ovsdb_server *);
+bool ovsdb_server_add_db(struct ovsdb_server *, struct ovsdb *);
 void ovsdb_server_destroy(struct ovsdb_server *);
 
 struct ovsdb_lock_waiter *ovsdb_server_lock(struct ovsdb_server *,
 void ovsdb_server_destroy(struct ovsdb_server *);
 
 struct ovsdb_lock_waiter *ovsdb_server_lock(struct ovsdb_server *,
index 6ae1f516b5754b89212607352a223714727dedd9..a93b84404eb91ec83d92e9449a5c897de7498c9c 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,12 +30,13 @@ static bool ovsdb_trigger_try(struct ovsdb_trigger *, long long int now);
 static void ovsdb_trigger_complete(struct ovsdb_trigger *);
 
 void
 static void ovsdb_trigger_complete(struct ovsdb_trigger *);
 
 void
-ovsdb_trigger_init(struct ovsdb_session *session,
+ovsdb_trigger_init(struct ovsdb_session *session, struct ovsdb *db,
                    struct ovsdb_trigger *trigger,
                    struct json *request, long long int now)
 {
     trigger->session = session;
                    struct ovsdb_trigger *trigger,
                    struct json *request, long long int now)
 {
     trigger->session = session;
-    list_push_back(&trigger->session->db->triggers, &trigger->node);
+    trigger->db = db;
+    list_push_back(&trigger->db->triggers, &trigger->node);
     trigger->request = request;
     trigger->result = NULL;
     trigger->created = now;
     trigger->request = request;
     trigger->result = NULL;
     trigger->created = now;
@@ -110,7 +111,7 @@ ovsdb_trigger_wait(struct ovsdb *db, long long int now)
 static bool
 ovsdb_trigger_try(struct ovsdb_trigger *t, long long int now)
 {
 static bool
 ovsdb_trigger_try(struct ovsdb_trigger *t, long long int now)
 {
-    t->result = ovsdb_execute(t->session->db, t->session,
+    t->result = ovsdb_execute(t->db, t->session,
                               t->request, now - t->created, &t->timeout_msec);
     if (t->result) {
         ovsdb_trigger_complete(t);
                               t->request, now - t->created, &t->timeout_msec);
     if (t->result) {
         ovsdb_trigger_complete(t);
index 1d47f3f09b62d89d024a250287fea5fb30b80035..f686b155edde678fff2df6a4fcf7dcbcb672cc64 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2011 Nicira, Inc.
+/* Copyright (c) 2009, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,7 +22,8 @@ struct ovsdb;
 
 struct ovsdb_trigger {
     struct ovsdb_session *session; /* Session that owns this trigger. */
 
 struct ovsdb_trigger {
     struct ovsdb_session *session; /* Session that owns this trigger. */
-    struct list node;           /* !result: in session->db->triggers;
+    struct ovsdb *db;           /* Database on which trigger acts. */
+    struct list node;           /* !result: in db->triggers;
                                  * result: in session->completions. */
     struct json *request;       /* Database request. */
     struct json *result;        /* Result (null if none yet). */
                                  * result: in session->completions. */
     struct json *request;       /* Database request. */
     struct json *result;        /* Result (null if none yet). */
@@ -30,7 +31,8 @@ struct ovsdb_trigger {
     long long int timeout_msec; /* Max wait duration. */
 };
 
     long long int timeout_msec; /* Max wait duration. */
 };
 
-void ovsdb_trigger_init(struct ovsdb_session *, struct ovsdb_trigger *,
+void ovsdb_trigger_init(struct ovsdb_session *, struct ovsdb *,
+                        struct ovsdb_trigger *,
                         struct json *request, long long int now);
 void ovsdb_trigger_destroy(struct ovsdb_trigger *);
 
                         struct json *request, long long int now);
 void ovsdb_trigger_destroy(struct ovsdb_trigger *);
 
index b0a3377bb493198c94010b864ac4971d3d48fb48..f787f5ace8d05c6cc2b06b7e51fc985a0d4e7c37 100644 (file)
@@ -142,12 +142,15 @@ AT_CLEANUP
 
 AT_SETUP([database multiplexing implementation])
 AT_KEYWORDS([ovsdb server positive])
 
 AT_SETUP([database multiplexing implementation])
 AT_KEYWORDS([ovsdb server positive])
-ordinal_schema > schema
-AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
-AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket db], [0], [ignore], [ignore])
+ordinal_schema > schema1
+constraint_schema > schema2
+AT_CHECK([ovsdb-tool create db1 schema1], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-tool create db2 schema2], [0], [ignore], [ignore])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --unixctl="`pwd`"/unixctl --remote=punix:socket db1 db2], [0], [ignore], [ignore])
 AT_CHECK(
   [[ovsdb-client list-dbs unix:socket]], 
 AT_CHECK(
   [[ovsdb-client list-dbs unix:socket]], 
-  [0], [ordinals
+  [0], [constraints
+ordinals
 ], [ignore], [test ! -e pid || kill `cat pid`])
 AT_CHECK(
   [[test-jsonrpc request unix:socket get_schema [\"nonexistent\"]]], [0],
 ], [ignore], [test ! -e pid || kill `cat pid`])
 AT_CHECK(
   [[test-jsonrpc request unix:socket get_schema [\"nonexistent\"]]], [0],
@@ -158,37 +161,67 @@ AT_CLEANUP
 
 AT_SETUP([--remote=db: implementation])
 AT_KEYWORDS([ovsdb server positive])
 
 AT_SETUP([--remote=db: implementation])
 AT_KEYWORDS([ovsdb server positive])
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
 AT_DATA([schema],
   [[{"name": "mydb",
      "tables": {
 AT_DATA([schema],
   [[{"name": "mydb",
      "tables": {
+       "Root": {
+         "columns": {
+           "managers": {
+             "type": {
+               "key": "string",
+               "min": 0,
+               "max": "unlimited"}},
+           "manager_options": {
+             "type": {
+               "key": {"type": "uuid", "refTable": "Manager"},
+               "min": 0,
+               "max": "unlimited"}}}},
        "Manager": {
          "columns": {
        "Manager": {
          "columns": {
-           "manager": {"type": "string"}}}}}
+           "target": {
+             "type": "string"},
+           "is_connected": {
+             "type": {
+               "key": "boolean",
+               "min": 0,
+               "max": 1}}}}}}
 ]])
 AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
 AT_CHECK(
   [[ovsdb-tool transact db \
      '["mydb",
 ]])
 AT_CHECK([ovsdb-tool create db schema], [0], [ignore], [ignore])
 AT_CHECK(
   [[ovsdb-tool transact db \
      '["mydb",
+       {"op": "insert",
+        "table": "Root",
+        "row": {
+          "managers": "punix:socket1",
+          "manager_options": ["set", [["named-uuid", "x"]]]}},
        {"op": "insert",
         "table": "Manager",
        {"op": "insert",
         "table": "Manager",
-        "row": {"manager": "punix:socket"}}]']], [0], [ignore], [ignore])
-AT_CHECK([ovsdb-server --detach --no-chdir --pidfile="`pwd`"/pid --remote=db:Manager,manager --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
+        "uuid-name": "x",
+        "row": {"target": "punix:socket2"}}]']], [0], [ignore], [ignore])
+ON_EXIT([kill `cat ovsdb-server.pid`])
+AT_CHECK([ovsdb-server --enable-dummy --detach --no-chdir --pidfile --remote=db:Root,managers --remote=db:Root,manager_options --log-file db], [0], [ignore], [ignore])
+for i in 1 2 3 4 5 6; do ovs-appctl -t ovsdb-server time/warp 1000; done
 AT_CHECK(
 AT_CHECK(
-  [[ovsdb-client transact unix:socket \
+  [[ovsdb-client transact unix:socket1 \
      '["mydb",
      '["mydb",
+       {"op": "select",
+        "table": "Root",
+        "where": [],
+        "columns": ["managers"]},
        {"op": "select",
         "table": "Manager",
         "where": [],
        {"op": "select",
         "table": "Manager",
         "where": [],
-        "columns": ["manager"]}]']], 
-  [0], [stdout], [ignore], [test ! -e pid || kill `cat pid`])
+        "columns": ["target", "is_connected"]}]']],
+  [0], [stdout], [ignore])
 AT_CHECK(
   [perl $srcdir/uuidfilt.pl stdout], 
   [0], 
 AT_CHECK(
   [perl $srcdir/uuidfilt.pl stdout], 
   [0], 
-  [[[{"rows":[{"manager":"punix:socket"}]}]
+  [[[{"rows":[{"managers":"punix:socket1"}]},{"rows":[{"is_connected":false,"target":"punix:socket2"}]}]
 ]], 
 ]], 
-  [ignore], 
-  [test ! -e pid || kill `cat pid`])
-OVSDB_SERVER_SHUTDOWN
+  [ignore])
 AT_CLEANUP
 
 AT_SETUP([SSL db: implementation])
 AT_CLEANUP
 
 AT_SETUP([SSL db: implementation])
index a1ad2cbc85655f49339ad540b78784380e6bc278..d448109c424a8254552b539aed96d4f80b57643f 100644 (file)
@@ -1294,6 +1294,7 @@ do_trigger(int argc OVS_UNUSED, char *argv[])
 {
     struct ovsdb_schema *schema;
     struct ovsdb_session session;
 {
     struct ovsdb_schema *schema;
     struct ovsdb_session session;
+    struct ovsdb_server server;
     struct json *json;
     struct ovsdb *db;
     long long int now;
     struct json *json;
     struct ovsdb *db;
     long long int now;
@@ -1306,7 +1307,9 @@ do_trigger(int argc OVS_UNUSED, char *argv[])
     json_destroy(json);
     db = ovsdb_create(schema);
 
     json_destroy(json);
     db = ovsdb_create(schema);
 
-    ovsdb_session_init(&session, db);
+    ovsdb_server_init(&server);
+    ovsdb_server_add_db(&server, db);
+    ovsdb_session_init(&session, &server);
 
     now = 0;
     number = 0;
 
     now = 0;
     number = 0;
@@ -1321,7 +1324,7 @@ do_trigger(int argc OVS_UNUSED, char *argv[])
             json_destroy(params);
         } else {
             struct test_trigger *t = xmalloc(sizeof *t);
             json_destroy(params);
         } else {
             struct test_trigger *t = xmalloc(sizeof *t);
-            ovsdb_trigger_init(&session, &t->trigger, params, now);
+            ovsdb_trigger_init(&session, db, &t->trigger, params, now);
             t->number = number++;
             if (ovsdb_trigger_is_complete(&t->trigger)) {
                 do_trigger_dump(t, now, "immediate");
             t->number = number++;
             if (ovsdb_trigger_is_complete(&t->trigger)) {
                 do_trigger_dump(t, now, "immediate");
@@ -1342,6 +1345,7 @@ do_trigger(int argc OVS_UNUSED, char *argv[])
         poll_block();
     }
 
         poll_block();
     }
 
+    ovsdb_server_destroy(&server);
     ovsdb_destroy(db);
 }
 
     ovsdb_destroy(db);
 }
 
index c730778d5fa665eb7f9d9fc3ed0dea45e4271e3a..22db06b2dfb593422e307d9193f41475a94fbaaa 100644 (file)
@@ -223,6 +223,12 @@ Overrides the file name for the Unix domain socket used to connect to
 .IP "\fB\-\-db\-schema=\fIschema\fR"
 Overrides the file name for the OVS database schema.
 .
 .IP "\fB\-\-db\-schema=\fIschema\fR"
 Overrides the file name for the OVS database schema.
 .
+.IP "\fB\-\-extra-dbs=\fIfile\fR"
+Adds \fIfile\fR as an extra database for \fBovsdb\-server\fR to serve
+out.  Multiple space-separated file names may also be specified.
+\fIfile\fR should begin with \fB/\fR; if it does not, then it will be
+taken as relative to \fIdbdir\fR.
+.
 .SH "The ``stop'' command"
 .
 .PP
 .SH "The ``stop'' command"
 .
 .PP
index 674c3c31e5c7e302a35b9876d2afdd32972dcaed..2aa63986a6d71adab1ad4062390f5efb6a8ab17a 100755 (executable)
@@ -189,9 +189,23 @@ start_ovsdb () {
 
         # Start ovsdb-server.
         set ovsdb-server "$DB_FILE"
 
         # Start ovsdb-server.
         set ovsdb-server "$DB_FILE"
+        for db in $EXTRA_DBS; do
+            case $db in
+                /*) ;;
+                *) db=$dbdir/$db ;;
+            esac
+
+            if test ! -f "$db"; then
+                log_warning_msg "$db (from \$EXTRA_DBS) does not exist."
+            elif ovsdb-tool db-version "$db" >/dev/null; then
+                set "$@" "$db"
+            else
+                log_warning_msg "$db (from \$EXTRA_DBS) cannot be read as a database (see error message above)"
+            fi
+        done
         set "$@" -vconsole:emer -vsyslog:err -vfile:info
         set "$@" --remote=punix:"$DB_SOCK"
         set "$@" -vconsole:emer -vsyslog:err -vfile:info
         set "$@" --remote=punix:"$DB_SOCK"
-        set "$@" --remote=db:Open_vSwitch,manager_options
+        set "$@" --remote=db:Open_vSwitch,Open_vSwitch,manager_options
         set "$@" --private-key=db:SSL,private_key
         set "$@" --certificate=db:SSL,certificate
         set "$@" --bootstrap-ca-cert=db:SSL,ca_cert
         set "$@" --private-key=db:SSL,private_key
         set "$@" --certificate=db:SSL,certificate
         set "$@" --bootstrap-ca-cert=db:SSL,ca_cert
@@ -406,6 +420,7 @@ set_defaults () {
     DB_FILE=$dbdir/conf.db
     DB_SOCK=$rundir/db.sock
     DB_SCHEMA=$datadir/vswitch.ovsschema
     DB_FILE=$dbdir/conf.db
     DB_SOCK=$rundir/db.sock
     DB_SCHEMA=$datadir/vswitch.ovsschema
+    EXTRA_DBS=
 
     PROTOCOL=gre
     DPORT=
 
     PROTOCOL=gre
     DPORT=