[ovs-dev] [multi-dbs 6/6] ovsdb-server: Add support for multiple databases.

Ethan Jackson ethan at nicira.com
Thu Sep 20 23:30:55 UTC 2012


In ovsdb_jsonrpc_session_got_request() we scope creating of a monitor
to a particular database, but don't scope deletions of monitors in the
same way.  Is it possible that two monitors of the same name would be
added to two different databases, and then a cancel could destroy the
wrong one?

Trailing whitespace in the prototype of parse_options() in ovsdb-server.c.

In update_remote_status() txn is used uninitialized in the call to
update_remote_rows().

Finally, it seems a bit awkward to me that we have to NULL terminate
the dbs list just so the unixctl callback doesn't need its length.
I'd slightly prefer to simply make dbs and n_dbs global.  That said,
it doesn't matter that much.

Ethan

On Fri, Sep 7, 2012 at 10:09 AM, Ben Pfaff <blp at nicira.com> wrote:
> 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 at nicira.com>
> ---
>  ovsdb/jsonrpc-server.c  |  115 ++++++++++++--------
>  ovsdb/jsonrpc-server.h  |    4 +-
>  ovsdb/ovsdb-server.1.in |   32 +++---
>  ovsdb/ovsdb-server.c    |  270 ++++++++++++++++++++++++++++++++---------------
>  ovsdb/server.c          |   28 ++++--
>  ovsdb/server.h          |   13 ++-
>  ovsdb/trigger.c         |    9 +-
>  ovsdb/trigger.h         |    8 +-
>  tests/ovsdb-server.at   |   11 ++-
>  tests/test-ovsdb.c      |    8 +-
>  utilities/ovs-ctl.8     |    6 +
>  utilities/ovs-ctl.in    |   17 +++-
>  12 files changed, 351 insertions(+), 170 deletions(-)
>
> diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c
> index bb887d0..279ea97 100644
> --- a/ovsdb/jsonrpc-server.c
> +++ b/ovsdb/jsonrpc-server.c
> @@ -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 *,
> +                                         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);
> @@ -76,7 +77,7 @@ static void ovsdb_jsonrpc_trigger_complete_done(
>
>  /* 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,
> @@ -106,16 +107,29 @@ static struct ovsdb_jsonrpc_remote *ovsdb_jsonrpc_server_add_remote(
>  );
>  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 *
> -ovsdb_jsonrpc_server_create(struct ovsdb *db)
> +ovsdb_jsonrpc_server_create(void)
>  {
>      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;
>  }
>
> +/* 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)
>  {
> @@ -350,7 +364,7 @@ ovsdb_jsonrpc_session_create(struct ovsdb_jsonrpc_remote *remote,
>      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);
> @@ -558,21 +572,22 @@ ovsdb_jsonrpc_session_get_status(const struct ovsdb_jsonrpc_remote *remote,
>      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;
> -    const char *want_db_name;
> -    const char *have_db_name;
>      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) {
> @@ -582,22 +597,23 @@ ovsdb_jsonrpc_check_db_name(const struct ovsdb_jsonrpc_session *s,
>          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",
> -            request->method, want_db_name);
> +            request->method, db_name);
>          goto error;
>      }
>
> -    return NULL;
> +    *replyp = NULL;
> +    return db;
>
>  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);
> -    return reply;
> +    return NULL;
>  }
>
>  static struct ovsdb_error *
> @@ -739,10 +755,10 @@ error:
>  }
>
>  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)
>  {
> -    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);
> @@ -756,30 +772,39 @@ ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s,
>      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) {
> -            reply = execute_transaction(s, request);
> +            reply = execute_transaction(s, db, request);
>          }
>      } 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(
> -                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")) {
> -        reply = ovsdb_jsonrpc_check_db_name(s, request);
> +        struct ovsdb *db = ovsdb_jsonrpc_lookup_db(s, request, &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")) {
> -        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")) {
> @@ -836,7 +861,7 @@ struct ovsdb_jsonrpc_trigger {
>  };
>
>  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;
> @@ -858,7 +883,7 @@ ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *s,
>
>      /* 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);
>
> @@ -962,6 +987,7 @@ struct ovsdb_jsonrpc_monitor_table {
>  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;
> @@ -1113,7 +1139,7 @@ ovsdb_jsonrpc_parse_monitor_request(struct ovsdb_jsonrpc_monitor_table *mt,
>  }
>
>  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;
> @@ -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);
> -    ovsdb_add_replica(s->remote->server->up.db, &m->replica);
> +    ovsdb_add_replica(db, &m->replica);
>      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);
> @@ -1154,7 +1181,7 @@ ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s,
>          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);
> @@ -1203,7 +1230,7 @@ ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s,
>
>  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);
> @@ -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 {
> -            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);
>          }
>      }
> @@ -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) {
> -        ovsdb_remove_replica(s->remote->server->up.db, &m->replica);
> +        ovsdb_remove_replica(m->db, &m->replica);
>      }
>  }
>
> diff --git a/ovsdb/jsonrpc-server.h b/ovsdb/jsonrpc-server.h
> index 2dc0c78..bf2a2fc 100644
> --- a/ovsdb/jsonrpc-server.h
> +++ b/ovsdb/jsonrpc-server.h
> @@ -22,7 +22,9 @@ struct ovsdb;
>  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. */
> diff --git a/ovsdb/ovsdb-server.1.in b/ovsdb/ovsdb-server.1.in
> index 1f5be03..b4b6eb6 100644
> --- a/ovsdb/ovsdb-server.1.in
> +++ b/ovsdb/ovsdb-server.1.in
> @@ -10,7 +10,7 @@ ovsdb\-server \- Open vSwitch database server
>  .
>  .SH SYNOPSIS
>  \fBovsdb\-server\fR
> -[\fIdatabase\fR]
> +[\fIdatabase\fR]\&...
>  [\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
> -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
> -The OVSDB file may be specified on the command line as \fIdatabase\fR.
> -The default is \fB at 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 at DBDIR@/conf.db\fR.  The database
> +files must already have been created and initialized using, for
>  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
>  .
> -.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
> -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
> @@ -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.
> -.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
> @@ -111,9 +112,10 @@ described below.
>  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
> diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
> index 1bf10d9..c1d01d1 100644
> --- a/ovsdb/ovsdb-server.c
> +++ b/ovsdb/ovsdb-server.c
> @@ -25,6 +25,7 @@
>  #include "command-line.h"
>  #include "daemon.h"
>  #include "dirs.h"
> +#include "dynamic-string.h"
>  #include "file.h"
>  #include "hash.h"
>  #include "json.h"
> @@ -55,6 +56,16 @@
>
>  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;
> @@ -65,17 +76,18 @@ static unixctl_cb_func ovsdb_server_exit;
>  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,
> -                                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,
> -                                 struct ovsdb *db);
> +                                 struct db dbs[], size_t n_dbs);
>
>  int
>  main(int argc, char *argv[])
> @@ -85,34 +97,53 @@ main(int argc, char *argv[])
>      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;
> -    char *file_name;
>      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();
>
> -    parse_options(argc, argv, &file_name, &remotes, &unixctl_path,
> -                  &run_command);
> +    parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command);
>
>      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) {
> @@ -145,28 +176,35 @@ main(int argc, char *argv[])
>      }
>
>      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) {
> +        int i;
> +
>          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);
>          }
>
> -        reconfigure_from_db(jsonrpc, db, &remotes);
> +        reconfigure_from_db(jsonrpc, dbs, n_dbs, &remotes);
>          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;
>          }
> @@ -174,13 +212,15 @@ main(int argc, char *argv[])
>          /* 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);
> -        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);
>          }
> @@ -191,7 +231,9 @@ main(int argc, char *argv[])
>          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);
>
> @@ -206,26 +248,64 @@ main(int argc, char *argv[])
>      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
> -parse_db_column(const struct ovsdb *db,
> +parse_db_column(const struct db dbs[], size_t n_dbs,
>                  const char *name_,
> +                const struct db **dbp,
>                  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 char *tokens[3];
>      char *save_ptr = NULL;
> +    const struct db *db;
> +    char *name;
>
>      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_);
>      }
> +    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 = ovsdb_get_table(db, table_name);
> +        table_name = tokens[0];
> +        column_name = tokens[1];
> +        db = &dbs[0];
> +    }
> +
> +    table = ovsdb_get_table(db->db, table_name);
>      if (!table) {
>          ovs_fatal(0, "\"%s\": no table named %s", name_, table_name);
>      }
> @@ -237,20 +317,23 @@ parse_db_column(const struct ovsdb *db,
>      }
>      free(name);
>
> +    *dbp = db;
>      *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 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 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) {
> @@ -259,12 +342,13 @@ parse_db_string_column(const struct ovsdb *db,
>                    name, table->schema->name, column->name);
>      }
>
> +    *dbp = db;
>      *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;
> @@ -272,8 +356,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 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;
> @@ -480,14 +565,15 @@ add_manager_options(struct shash *remotes, const struct ovsdb_row *row)
>  }
>
>  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;
> +    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) {
> @@ -581,19 +667,20 @@ update_remote_row(const struct ovsdb_row *row, struct ovsdb_txn *txn,
>  }
>
>  static void
> -update_remote_rows(const struct ovsdb *db, struct ovsdb_txn *txn,
> +update_remote_rows(const struct db dbs[], size_t n_dbs, struct ovsdb_txn *txn,
>                     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;
>      }
>
> -    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
> @@ -621,32 +708,37 @@ update_remote_rows(const struct ovsdb *db, struct ovsdb_txn *txn,
>
>  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);
>      struct ovsdb_txn *txn;
> -    const bool durable_txn = false;
> -    struct ovsdb_error *error;
>      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) {
> -        update_remote_rows(db, txn, remote, jsonrpc);
> +        update_remote_rows(dbs, n_dbs, txn, 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,
> -                    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;
> @@ -655,7 +747,7 @@ reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
>      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);
>          }
> @@ -664,9 +756,9 @@ reconfigure_from_db(struct ovsdb_jsonrpc_server *jsonrpc,
>      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);
>  }
>
> @@ -681,22 +773,42 @@ ovsdb_server_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
>  }
>
>  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 {
> -        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
> @@ -712,9 +824,8 @@ ovsdb_server_reconnect(struct unixctl_conn *conn, int argc OVS_UNUSED,
>  }
>
>  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_DUMMY = UCHAR_MAX + 1,
> @@ -742,6 +853,8 @@ parse_options(int argc, char *argv[], char **file_namep,
>          {NULL, 0, NULL, 0},
>      };
>      char *short_options = long_options_to_short_options(long_options);
> +    int argc = *argcp;
> +    char **argv = *argvp;
>
>      sset_init(remotes);
>      for (;;) {
> @@ -803,31 +916,18 @@ parse_options(int argc, char *argv[], char **file_namep,
>      }
>      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"
> -           "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);
> diff --git a/ovsdb/server.c b/ovsdb/server.c
> index 7cd4263..ac2aa29 100644
> --- a/ovsdb/server.c
> +++ b/ovsdb/server.c
> @@ -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.
> @@ -20,12 +20,13 @@
>  #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
> -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);
>  }
> @@ -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);
>  }
>
> -/* 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
> -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);
>  }
>
> +/* 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)
>  {
> +    shash_destroy(&server->dbs);
>      hmap_destroy(&server->locks);
>  }
>
> diff --git a/ovsdb/server.h b/ovsdb/server.h
> index e073850..561f01e 100644
> --- a/ovsdb/server.h
> +++ b/ovsdb/server.h
> @@ -18,17 +18,21 @@
>
>  #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 {
> -    struct ovsdb *db;
> +    struct ovsdb_server *server;
>      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(
> @@ -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 {
> -    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. */
>  };
>
> -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 *,
> diff --git a/ovsdb/trigger.c b/ovsdb/trigger.c
> index 6ae1f51..a93b844 100644
> --- a/ovsdb/trigger.c
> +++ b/ovsdb/trigger.c
> @@ -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.
> @@ -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
> -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;
> -    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;
> @@ -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)
>  {
> -    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);
> diff --git a/ovsdb/trigger.h b/ovsdb/trigger.h
> index 1d47f3f..f686b15 100644
> --- a/ovsdb/trigger.h
> +++ b/ovsdb/trigger.h
> @@ -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.
> @@ -22,7 +22,8 @@ struct ovsdb;
>
>  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). */
> @@ -30,7 +31,8 @@ struct ovsdb_trigger {
>      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 *);
>
> diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
> index b0a3377..65fb6b2 100644
> --- a/tests/ovsdb-server.at
> +++ b/tests/ovsdb-server.at
> @@ -142,12 +142,15 @@ AT_CLEANUP
>
>  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]],
> -  [0], [ordinals
> +  [0], [constraints
> +ordinals
>  ], [ignore], [test ! -e pid || kill `cat pid`])
>  AT_CHECK(
>    [[test-jsonrpc request unix:socket get_schema [\"nonexistent\"]]], [0],
> diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
> index a1ad2cb..d448109 100644
> --- a/tests/test-ovsdb.c
> +++ b/tests/test-ovsdb.c
> @@ -1294,6 +1294,7 @@ do_trigger(int argc OVS_UNUSED, char *argv[])
>  {
>      struct ovsdb_schema *schema;
>      struct ovsdb_session session;
> +    struct ovsdb_server server;
>      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);
>
> -    ovsdb_session_init(&session, db);
> +    ovsdb_server_init(&server);
> +    ovsdb_server_add_db(&server, db);
> +    ovsdb_session_init(&session, &server);
>
>      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);
> -            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");
> @@ -1342,6 +1345,7 @@ do_trigger(int argc OVS_UNUSED, char *argv[])
>          poll_block();
>      }
>
> +    ovsdb_server_destroy(&server);
>      ovsdb_destroy(db);
>  }
>
> diff --git a/utilities/ovs-ctl.8 b/utilities/ovs-ctl.8
> index c730778..22db06b 100644
> --- a/utilities/ovs-ctl.8
> +++ b/utilities/ovs-ctl.8
> @@ -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\-\-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
> diff --git a/utilities/ovs-ctl.in b/utilities/ovs-ctl.in
> index 674c3c3..2aa6398 100755
> --- a/utilities/ovs-ctl.in
> +++ b/utilities/ovs-ctl.in
> @@ -189,9 +189,23 @@ start_ovsdb () {
>
>          # 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 "$@" --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
> @@ -406,6 +420,7 @@ set_defaults () {
>      DB_FILE=$dbdir/conf.db
>      DB_SOCK=$rundir/db.sock
>      DB_SCHEMA=$datadir/vswitch.ovsschema
> +    EXTRA_DBS=
>
>      PROTOCOL=gre
>      DPORT=
> --
> 1.7.2.5
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev



More information about the dev mailing list