/* 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);
/* 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 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)
{
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);
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) {
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 *
}
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);
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")) {
};
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;
/* 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);
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;
}
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;
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);
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);
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);
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);
}
}
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);
}
}
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. */
.
.SH SYNOPSIS
\fBovsdb\-server\fR
-[\fIdatabase\fR]
+[\fIdatabase\fR]\&...
[\fB\-\-remote=\fIremote\fR]\&...
[\fB\-\-run=\fIcommand\fR]
.so lib/daemon-syn.man
.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@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
.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
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
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
#include "daemon.h"
#include "dirs.h"
#include "dummy.h"
+#include "dynamic-string.h"
#include "file.h"
#include "hash.h"
#include "json.h"
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;
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[])
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) {
}
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;
}
/* 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);
}
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);
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_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);
}
}
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) {
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;
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;
}
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) {
}
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 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
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);
}
}
}
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, 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;
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);
}
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);
}
}
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
}
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,
{NULL, 0, NULL, 0},
};
char *short_options = long_options_to_short_options(long_options);
+ int argc = *argcp;
+ char **argv = *argvp;
sset_init(remotes);
for (;;) {
}
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);
-/* 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.
#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);
}
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);
}
#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(
* 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 *,
-/* 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.
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;
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);
-/* 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.
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). */
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 *);
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],
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": {
+ "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": {"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",
+ {"op": "insert",
+ "table": "Root",
+ "row": {
+ "managers": "punix:socket1",
+ "manager_options": ["set", [["named-uuid", "x"]]]}},
{"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(
- [[ovsdb-client transact unix:socket \
+ [[ovsdb-client transact unix:socket1 \
'["mydb",
+ {"op": "select",
+ "table": "Root",
+ "where": [],
+ "columns": ["managers"]},
{"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],
- [[[{"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])
{
struct ovsdb_schema *schema;
struct ovsdb_session session;
+ struct ovsdb_server server;
struct json *json;
struct ovsdb *db;
long long int now;
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;
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");
poll_block();
}
+ ovsdb_server_destroy(&server);
ovsdb_destroy(db);
}
.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
# 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
DB_FILE=$dbdir/conf.db
DB_SOCK=$rundir/db.sock
DB_SCHEMA=$datadir/vswitch.ovsschema
+ EXTRA_DBS=
PROTOCOL=gre
DPORT=