X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ovsdb%2Fovsdb-server.c;h=b31560b3aa5e5219b9e2de95248e632c0ae746fe;hb=825da1c6d1c7b9bc5128d7588b8bad6efaae650e;hp=2657e26419c5d581ad39b362a3a49a9cadbfe7dc;hpb=3e6c955de6fc7c50ea30ee9ea2148c6734e5df11;p=sliver-openvswitch.git diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c index 2657e2641..b31560b3a 100644 --- a/ovsdb/ovsdb-server.c +++ b/ovsdb/ovsdb-server.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. +/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -31,7 +32,6 @@ #include "json.h" #include "jsonrpc.h" #include "jsonrpc-server.h" -#include "leak-checker.h" #include "list.h" #include "memory.h" #include "ovsdb.h" @@ -76,6 +76,21 @@ static unixctl_cb_func ovsdb_server_exit; static unixctl_cb_func ovsdb_server_compact; static unixctl_cb_func ovsdb_server_reconnect; +struct add_remote_aux { + struct sset *remotes; + struct db *dbs; + size_t n_dbs; + FILE *config_tmpfile; +}; +static unixctl_cb_func ovsdb_server_add_remote; + +struct remove_remote_aux { + struct sset *remotes; + FILE *config_tmpfile; +}; +static unixctl_cb_func ovsdb_server_remove_remote; +static unixctl_cb_func ovsdb_server_list_remotes; + static void parse_options(int *argc, char **argvp[], struct sset *remotes, char **unixctl_pathp, char **run_command); @@ -89,6 +104,9 @@ static void update_remote_status(const struct ovsdb_jsonrpc_server *jsonrpc, const struct sset *remotes, struct db dbs[], size_t n_dbs); +static void save_config(FILE *config_file, const struct sset *); +static void load_config(FILE *config_file, struct sset *); + int main(int argc, char *argv[]) { @@ -101,6 +119,9 @@ main(int argc, char *argv[]) bool exiting; int retval; long long int status_timer = LLONG_MIN; + struct add_remote_aux add_remote_aux; + struct remove_remote_aux remove_remote_aux; + FILE *config_tmpfile; struct db *dbs; int n_dbs; @@ -114,8 +135,22 @@ main(int argc, char *argv[]) parse_options(&argc, &argv, &remotes, &unixctl_path, &run_command); + /* Create and initialize 'config_tmpfile' as a temporary file to hold + * ovsdb-server's most basic configuration, and then save our initial + * configuration to it. When --monitor is used, this preserves the effects + * of ovs-appctl commands such as ovsdb-server/add-remote (which saves the + * new configuration) across crashes. */ + config_tmpfile = tmpfile(); + if (!config_tmpfile) { + ovs_fatal(errno, "failed to create temporary file"); + } + save_config(config_tmpfile, &remotes); + daemonize_start(); + /* Load the saved config. */ + load_config(config_tmpfile, &remotes); + n_dbs = MAX(1, argc); dbs = xcalloc(n_dbs + 1, sizeof *dbs); if (argc > 0) { @@ -158,7 +193,7 @@ main(int argc, char *argv[]) run_argv[2] = run_command; run_argv[3] = NULL; - retval = process_start(run_argv, NULL, 0, NULL, 0, &run_process); + retval = process_start(run_argv, &run_process); if (retval) { ovs_fatal(retval, "%s: process failed to start", run_command); } @@ -181,6 +216,21 @@ main(int argc, char *argv[]) unixctl_command_register("ovsdb-server/reconnect", "", 0, 0, ovsdb_server_reconnect, jsonrpc); + add_remote_aux.remotes = &remotes; + add_remote_aux.dbs = dbs; + add_remote_aux.n_dbs = n_dbs; + add_remote_aux.config_tmpfile = config_tmpfile; + unixctl_command_register("ovsdb-server/add-remote", "REMOTE", 1, 1, + ovsdb_server_add_remote, &add_remote_aux); + + remove_remote_aux.remotes = &remotes; + remove_remote_aux.config_tmpfile = config_tmpfile; + unixctl_command_register("ovsdb-server/remove-remote", "REMOTE", 1, 1, + ovsdb_server_remove_remote, &remove_remote_aux); + + unixctl_command_register("ovsdb-server/list-remotes", "", 0, 0, + ovsdb_server_list_remotes, &remotes); + exiting = false; while (!exiting) { int i; @@ -198,15 +248,22 @@ main(int argc, char *argv[]) simap_destroy(&usage); } + /* Run unixctl_server_run() before reconfigure_from_db() because + * ovsdb-server/add-remote and ovsdb-server/remove-remote can change + * the set of remotes that reconfigure_from_db() uses. */ + unixctl_server_run(unixctl); + reconfigure_from_db(jsonrpc, dbs, n_dbs, &remotes); ovsdb_jsonrpc_server_run(jsonrpc); - unixctl_server_run(unixctl); 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_run(); + if (process_exited(run_process)) { + exiting = true; + } } /* update Manager status(es) every 5 seconds */ @@ -262,12 +319,12 @@ find_db(const struct db dbs[], size_t n_dbs, const char *db_name) return NULL; } -static void -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) +static char * WARN_UNUSED_RESULT +parse_db_column__(const struct db dbs[], size_t n_dbs, + const char *name_, char *name, + const struct db **dbp, + const struct ovsdb_table **tablep, + const struct ovsdb_column **columnp) { const char *table_name, *column_name; const struct ovsdb_column *column; @@ -275,15 +332,17 @@ parse_db_column(const struct db dbs[], size_t n_dbs, const char *tokens[3]; char *save_ptr = NULL; const struct db *db; - char *name; - name = xstrdup(name_); + *dbp = NULL; + *tablep = NULL; + *columnp = NULL; + strtok_r(name, ":", &save_ptr); /* "db:" */ 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_); + return xasprintf("\"%s\": invalid syntax", name_); } if (tokens[2]) { const char *db_name = tokens[0]; @@ -292,12 +351,13 @@ parse_db_column(const struct db dbs[], size_t n_dbs, db = find_db(dbs, n_dbs, tokens[0]); if (!db) { - ovs_fatal(0, "\"%s\": no database named %s", name_, db_name); + return xasprintf("\"%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_); + return xasprintf("\"%s\": database name must be specified " + "(because multiple databases are configured)", + name_); } table_name = tokens[0]; @@ -307,44 +367,61 @@ parse_db_column(const struct db dbs[], size_t n_dbs, table = ovsdb_get_table(db->db, table_name); if (!table) { - ovs_fatal(0, "\"%s\": no table named %s", name_, table_name); + return xasprintf("\"%s\": no table named %s", name_, table_name); } column = ovsdb_table_schema_get_column(table->schema, column_name); if (!column) { - ovs_fatal(0, "\"%s\": table \"%s\" has no column \"%s\"", - name_, table_name, column_name); + return xasprintf("\"%s\": table \"%s\" has no column \"%s\"", + name_, table_name, column_name); } - free(name); *dbp = db; *columnp = column; *tablep = table; + return NULL; } -static void +/* Returns NULL if successful, otherwise a malloc()'d string describing the + * error. */ +static char * WARN_UNUSED_RESULT +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 = xstrdup(name_); + char *retval = parse_db_column__(dbs, n_dbs, name_, name, + dbp, tablep, columnp); + free(name); + return retval; +} + +/* Returns NULL if successful, otherwise a malloc()'d string describing the + * error. */ +static char * WARN_UNUSED_RESULT 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; + char *retval; - parse_db_column(dbs, n_dbs, name, &db, &table, &column); + retval = parse_db_column(dbs, n_dbs, name, dbp, tablep, columnp); + if (retval) { + return retval; + } - if (column->type.key.type != OVSDB_TYPE_STRING - || column->type.value.type != OVSDB_TYPE_VOID) { - ovs_fatal(0, "\"%s\": table \"%s\" column \"%s\" is " - "not string or set of strings", - name, table->schema->name, column->name); + if ((*columnp)->type.key.type != OVSDB_TYPE_STRING + || (*columnp)->type.value.type != OVSDB_TYPE_VOID) { + return xasprintf("\"%s\": table \"%s\" column \"%s\" is " + "not string or set of strings", + name, (*tablep)->schema->name, (*columnp)->name); } - *dbp = db; - *columnp = column; - *tablep = table; + return NULL; } static OVS_UNUSED const char * @@ -357,8 +434,13 @@ query_db_string(const struct db dbs[], size_t n_dbs, const char *name) const struct ovsdb_table *table; const struct ovsdb_row *row; const struct db *db; + char *retval; - parse_db_string_column(dbs, n_dbs, name, &db, &table, &column); + retval = parse_db_string_column(dbs, n_dbs, name, + &db, &table, &column); + if (retval) { + ovs_fatal(0, "%s", retval); + } HMAP_FOR_EACH (row, hmap_node, &table->rows) { const struct ovsdb_datum *datum; @@ -588,8 +670,12 @@ query_db_remotes(const char *name, const struct db dbs[], size_t n_dbs, const struct ovsdb_table *table; const struct ovsdb_row *row; const struct db *db; + char *retval; - parse_db_column(dbs, n_dbs, name, &db, &table, &column); + retval = parse_db_column(dbs, n_dbs, name, &db, &table, &column); + if (retval) { + ovs_fatal(0, "%s", retval); + } if (column->type.key.type == OVSDB_TYPE_STRING && column->type.value.type == OVSDB_TYPE_VOID) { @@ -630,7 +716,7 @@ update_remote_row(const struct ovsdb_row *row, struct ovsdb_txn *txn, struct ovsdb_jsonrpc_remote_status status; struct ovsdb_row *rw_row; const char *target; - char *keys[8], *values[8]; + char *keys[9], *values[9]; size_t n = 0; /* Get the "target" (protocol/host/port) spec. */ @@ -677,6 +763,10 @@ update_remote_row(const struct ovsdb_row *row, struct ovsdb_txn *txn, keys[n] = xstrdup("n_connections"); values[n++] = xasprintf("%d", status.n_connections); } + if (status.bound_port != htons(0)) { + keys[n] = xstrdup("bound_port"); + values[n++] = xasprintf("%"PRIu16, ntohs(status.bound_port)); + } write_string_string_column(rw_row, "status", keys, values, n); ovsdb_jsonrpc_server_free_remote_status(&status); @@ -691,12 +781,16 @@ update_remote_rows(const struct db dbs[], size_t n_dbs, const struct ovsdb_column *column; const struct ovsdb_row *row; const struct db *db; + char *retval; if (strncmp("db:", remote_name, 3)) { return; } - parse_db_column(dbs, n_dbs, remote_name, &db, &table, &column); + retval = parse_db_column(dbs, n_dbs, remote_name, &db, &table, &column); + if (retval) { + ovs_fatal(0, "%s", retval); + } if (column->type.key.type != OVSDB_TYPE_UUID || !column->type.key.u.uuid.refTable @@ -838,6 +932,75 @@ ovsdb_server_reconnect(struct unixctl_conn *conn, int argc OVS_UNUSED, unixctl_command_reply(conn, NULL); } +/* "ovsdb-server/add-remote REMOTE": adds REMOTE to the set of remotes that + * ovsdb-server services. */ +static void +ovsdb_server_add_remote(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[], void *aux_) +{ + struct add_remote_aux *aux = aux_; + const char *remote = argv[1]; + + const struct ovsdb_column *column; + const struct ovsdb_table *table; + const struct db *db; + char *retval; + + retval = (strncmp("db:", remote, 3) + ? NULL + : parse_db_column(aux->dbs, aux->n_dbs, remote, + &db, &table, &column)); + if (!retval) { + if (sset_add(aux->remotes, remote)) { + save_config(aux->config_tmpfile, aux->remotes); + } + unixctl_command_reply(conn, NULL); + } else { + unixctl_command_reply_error(conn, retval); + free(retval); + } +} + +/* "ovsdb-server/remove-remote REMOTE": removes REMOTE frmo the set of remotes + * that ovsdb-server services. */ +static void +ovsdb_server_remove_remote(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[], void *aux_) +{ + struct remove_remote_aux *aux = aux_; + struct sset_node *node; + + node = sset_find(aux->remotes, argv[1]); + if (node) { + sset_delete(aux->remotes, node); + save_config(aux->config_tmpfile, aux->remotes); + unixctl_command_reply(conn, NULL); + } else { + unixctl_command_reply_error(conn, "no such remote"); + } +} + +/* "ovsdb-server/list-remotes": outputs a list of configured rmeotes. */ +static void +ovsdb_server_list_remotes(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *remotes_) +{ + struct sset *remotes = remotes_; + const char **list, **p; + struct ds s; + + ds_init(&s); + + list = sset_sort(remotes); + for (p = list; *p; p++) { + ds_put_format(&s, "%s\n", *p); + } + free(list); + + unixctl_command_reply(conn, ds_cstr(&s)); + ds_destroy(&s); +} + static void parse_options(int *argcp, char **argvp[], struct sset *remotes, char **unixctl_pathp, char **run_command) @@ -849,10 +1012,9 @@ parse_options(int *argcp, char **argvp[], OPT_BOOTSTRAP_CA_CERT, OPT_ENABLE_DUMMY, VLOG_OPTION_ENUMS, - LEAK_CHECKER_OPTION_ENUMS, DAEMON_OPTION_ENUMS }; - static struct option long_options[] = { + static const struct option long_options[] = { {"remote", required_argument, NULL, OPT_REMOTE}, {"unixctl", required_argument, NULL, OPT_UNIXCTL}, {"run", required_argument, NULL, OPT_RUN}, @@ -860,7 +1022,6 @@ parse_options(int *argcp, char **argvp[], {"version", no_argument, NULL, 'V'}, DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, - LEAK_CHECKER_LONG_OPTIONS, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, {"private-key", required_argument, NULL, 'p'}, {"certificate", required_argument, NULL, 'c'}, @@ -903,7 +1064,6 @@ parse_options(int *argcp, char **argvp[], VLOG_OPTION_HANDLERS DAEMON_OPTION_HANDLERS - LEAK_CHECKER_OPTION_HANDLERS case 'p': private_key_file = optarg; @@ -958,6 +1118,58 @@ usage(void) " --unixctl=SOCKET override default control socket name\n" " -h, --help display this help message\n" " -V, --version display version information\n"); - leak_checker_usage(); exit(EXIT_SUCCESS); } + +/* Truncates and replaces the contents of 'config_file' by a representation + * of 'remotes'. */ +static void +save_config(FILE *config_file, const struct sset *remotes) +{ + const char *remote; + struct json *json; + char *s; + + if (ftruncate(fileno(config_file), 0) == -1) { + VLOG_FATAL("failed to truncate temporary file (%s)", strerror(errno)); + } + + json = json_array_create_empty(); + SSET_FOR_EACH (remote, remotes) { + json_array_add(json, json_string_create(remote)); + } + s = json_to_string(json, 0); + json_destroy(json); + + if (fseek(config_file, 0, SEEK_SET) != 0 + || fputs(s, config_file) == EOF + || fflush(config_file) == EOF) { + VLOG_FATAL("failed to write temporary file (%s)", strerror(errno)); + } + free(s); +} + +/* Clears and replaces 'remotes' by a configuration read from 'config_file', + * which must have been previously written by save_config(). */ +static void +load_config(FILE *config_file, struct sset *remotes) +{ + struct json *json; + size_t i; + + sset_clear(remotes); + + if (fseek(config_file, 0, SEEK_SET) != 0) { + VLOG_FATAL("seek failed in temporary file (%s)", strerror(errno)); + } + json = json_from_stream(config_file); + if (json->type == JSON_STRING) { + VLOG_FATAL("reading json failed (%s)", json_string(json)); + } + ovs_assert(json->type == JSON_ARRAY); + for (i = 0; i < json->u.array.n; i++) { + const struct json *remote = json->u.array.elems[i]; + sset_add(remotes, json_string(remote)); + } + json_destroy(json); +}