In an upcoming commit, this will allow adding daemon mode to ovn-sbctl without having a lot of duplicated code.
Signed-off-by: Ben Pfaff <[email protected]> --- utilities/automake.mk | 5 +- utilities/ovn-dbctl.c | 1214 ++++++++++++++++++++++++++++++++++++ utilities/ovn-dbctl.h | 60 ++ utilities/ovn-nbctl.c | 1366 ++++------------------------------------- 4 files changed, 1411 insertions(+), 1234 deletions(-) create mode 100644 utilities/ovn-dbctl.c create mode 100644 utilities/ovn-dbctl.h diff --git a/utilities/automake.mk b/utilities/automake.mk index c4a6d248c274..50c0cfded018 100644 --- a/utilities/automake.mk +++ b/utilities/automake.mk @@ -71,7 +71,10 @@ utilities/ovn-lib: $(top_builddir)/config.status # ovn-nbctl bin_PROGRAMS += utilities/ovn-nbctl -utilities_ovn_nbctl_SOURCES = utilities/ovn-nbctl.c +utilities_ovn_nbctl_SOURCES = \ + utilities/ovn-dbctl.c \ + utilities/ovn-dbctl.h \ + utilities/ovn-nbctl.c utilities_ovn_nbctl_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la $(OVS_LIBDIR)/libopenvswitch.la # ovn-sbctl diff --git a/utilities/ovn-dbctl.c b/utilities/ovn-dbctl.c new file mode 100644 index 000000000000..28ebc6267066 --- /dev/null +++ b/utilities/ovn-dbctl.c @@ -0,0 +1,1214 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> + +#include "ovn-dbctl.h" + +#include <getopt.h> + +#include "command-line.h" +#include "daemon.h" +#include "db-ctl-base.h" +#include "fatal-signal.h" +#include "jsonrpc.h" +#include "memory.h" +#include "openvswitch/poll-loop.h" +#include "openvswitch/vlog.h" +#include "ovn-util.h" +#include "ovsdb-idl.h" +#include "process.h" +#include "simap.h" +#include "stream-ssl.h" +#include "svec.h" +#include "table.h" +#include "timer.h" +#include "unixctl.h" +#include "util.h" + +VLOG_DEFINE_THIS_MODULE(ovn_dbctl); + +/* --db: The database server to contact. */ +static const char *db; + +/* --oneline: Write each command's output as a single line? */ +static bool oneline; + +/* --dry-run: Do not commit any changes. */ +static bool dry_run; + +/* --wait=TYPE: Wait for configuration change to take effect? */ +static enum nbctl_wait_type wait_type = NBCTL_WAIT_NONE; + +static bool print_wait_time = false; + +/* --timeout: Time to wait for a connection to 'db'. */ +static unsigned int timeout; + +/* Format for table output. */ +static struct table_style table_style = TABLE_STYLE_DEFAULT; + +/* The IDL we're using and the current transaction, if any. This is for use by + * ovn_dbctl_exit() only, to allow it to clean up. Other code should use its + * context arguments. */ +static struct ovsdb_idl *the_idl; +static struct ovsdb_idl_txn *the_idl_txn; + +/* --leader-only, --no-leader-only: Only accept the leader in a cluster. */ +static int leader_only = true; + +/* --shuffle-remotes, --no-shuffle-remotes: Shuffle the order of remotes that + * are specified in the connetion method string. */ +static int shuffle_remotes = true; + +/* --unixctl-path: Path to use for unixctl server socket, for daemon mode. */ +static char *unixctl_path; + +static unixctl_cb_func server_cmd_exit; +static unixctl_cb_func server_cmd_run; + +static struct option *get_all_options(void); +static bool has_option(const struct ovs_cmdl_parsed_option *, size_t n, + int option); +static void dbctl_client(const struct ovn_dbctl_options *dbctl_options, + const char *socket_name, + const struct ovs_cmdl_parsed_option *, size_t n, + int argc, char *argv[]); +static bool will_detach(const struct ovs_cmdl_parsed_option *, size_t n); +static void apply_options_direct(const struct ovn_dbctl_options *dbctl_options, + const struct ovs_cmdl_parsed_option *, + size_t n, struct shash *local_options); +static char * OVS_WARN_UNUSED_RESULT run_prerequisites( + const struct ovn_dbctl_options *dbctl_options, + struct ctl_command[], size_t n_commands, struct ovsdb_idl *); +static char * OVS_WARN_UNUSED_RESULT do_dbctl( + const struct ovn_dbctl_options *dbctl_options, + const char *args, struct ctl_command *, size_t n, + struct ovsdb_idl *, const struct timer *, bool *retry); +static char * OVS_WARN_UNUSED_RESULT main_loop( + const struct ovn_dbctl_options *, const char *args, + struct ctl_command *commands, size_t n_commands, + struct ovsdb_idl *idl, const struct timer *); +static void server_loop(const struct ovn_dbctl_options *dbctl_options, + struct ovsdb_idl *idl, int argc, char *argv[]); +static void ovn_dbctl_exit(int status); + +int +ovn_dbctl_main(int argc, char *argv[], + const struct ovn_dbctl_options *dbctl_options) +{ + struct ovsdb_idl *idl; + struct shash local_options; + + ovn_set_program_name(argv[0]); + fatal_ignore_sigpipe(); + vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); + vlog_set_levels_from_string_assert("reconnect:warn"); + + ctl_init__(dbctl_options->idl_class, + dbctl_options->tables, + dbctl_options->cmd_show_table, + ovn_dbctl_exit); + ctl_register_commands(dbctl_options->commands); + + /* Check if options are set via env var. */ + char **argv_ = ovs_cmdl_env_parse_all( + &argc, argv, getenv(dbctl_options->options_env_var_name)); + + /* This utility has three operation modes: + * + * - Direct: Executes commands by contacting ovsdb-server directly. + * + * - Server: Runs in the background as a daemon waiting for requests + * from a process running in client mode. + * + * - Client: Executes commands by passing them to a process running in + * the server mode. + * + * At this point we don't know what mode we're running in. The mode partly + * depends on the command line. So, for now we transform the command line + * into a parsed form, and figure out what to do with it later. + */ + struct ovs_cmdl_parsed_option *parsed_options; + size_t n_parsed_options; + char *error_s = ovs_cmdl_parse_all(argc, argv_, get_all_options(), + &parsed_options, &n_parsed_options); + if (error_s) { + ctl_fatal("%s", error_s); + } + + /* Now figure out the operation mode: + * + * - A --detach option implies server mode. + * + * - An OVN_??_DAEMON environment variable implies client mode. + * + * - Otherwise, we're in direct mode. */ + const char *socket_name = (unixctl_path ? unixctl_path + : getenv(dbctl_options->daemon_env_var_name)); + if (((socket_name && socket_name[0]) + || has_option(parsed_options, n_parsed_options, 'u')) + && !will_detach(parsed_options, n_parsed_options)) { + dbctl_client(dbctl_options, socket_name, + parsed_options, n_parsed_options, argc, argv_); + } + + /* Parse command line. */ + shash_init(&local_options); + apply_options_direct(dbctl_options, + parsed_options, n_parsed_options, &local_options); + free(parsed_options); + + bool daemon_mode = false; + if (get_detach()) { + if (argc != optind) { + ctl_fatal("non-option arguments not supported with --detach " + "(use --help for help)"); + } + daemon_mode = true; + } + /* Initialize IDL. */ + idl = the_idl = ovsdb_idl_create_unconnected(dbctl_options->idl_class, + true); + ovsdb_idl_set_shuffle_remotes(idl, shuffle_remotes); + /* "retry" is true iff in daemon mode. */ + ovsdb_idl_set_remote(idl, db, daemon_mode); + ovsdb_idl_set_leader_only(idl, leader_only); + + if (daemon_mode) { + server_loop(dbctl_options, idl, argc, argv_); + } else { + struct ctl_command *commands; + size_t n_commands; + char *error; + + error = ctl_parse_commands(argc - optind, argv_ + optind, + &local_options, &commands, &n_commands); + if (error) { + ctl_fatal("%s", error); + } + + char *args = process_escape_args(argv_); + VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG, + "Called as %s", args); + + ctl_timeout_setup(timeout); + + error = run_prerequisites(dbctl_options, commands, n_commands, idl); + if (error) { + goto cleanup; + } + + error = main_loop(dbctl_options, args, commands, n_commands, idl, NULL); + +cleanup: + free(args); + + struct ctl_command *c; + for (c = commands; c < &commands[n_commands]; c++) { + ds_destroy(&c->output); + table_destroy(c->table); + free(c->table); + shash_destroy_free_data(&c->options); + } + free(commands); + if (error) { + ctl_fatal("%s", error); + } + } + + ovsdb_idl_destroy(idl); + idl = the_idl = NULL; + + for (int i = 0; i < argc; i++) { + free(argv_[i]); + } + free(argv_); + exit(EXIT_SUCCESS); +} + +static char * +main_loop(const struct ovn_dbctl_options *dbctl_options, + const char *args, struct ctl_command *commands, size_t n_commands, + struct ovsdb_idl *idl, const struct timer *wait_timeout) +{ + unsigned int seqno; + bool idl_ready; + + /* Execute the commands. + * + * 'seqno' is the database sequence number for which we last tried to + * execute our transaction. There's no point in trying to commit more than + * once for any given sequence number, because if the transaction fails + * it's because the database changed and we need to obtain an up-to-date + * view of the database before we try the transaction again. */ + seqno = ovsdb_idl_get_seqno(idl); + + /* IDL might have already obtained the database copy during previous + * invocation. If so, we can't expect the sequence number to change before + * we issue any new requests. */ + idl_ready = ovsdb_idl_has_ever_connected(idl); + for (;;) { + ovsdb_idl_run(idl); + if (!ovsdb_idl_is_alive(idl)) { + int retval = ovsdb_idl_get_last_error(idl); + ctl_fatal("%s: database connection failed (%s)", + db, ovs_retval_to_string(retval)); + } + + if (idl_ready || seqno != ovsdb_idl_get_seqno(idl)) { + idl_ready = false; + seqno = ovsdb_idl_get_seqno(idl); + + bool retry; + char *error = do_dbctl(dbctl_options, + args, commands, n_commands, idl, + wait_timeout, &retry); + if (error) { + return error; + } + if (!retry) { + return NULL; + } + } + + if (seqno == ovsdb_idl_get_seqno(idl)) { + ovsdb_idl_wait(idl); + poll_block(); + } + } + + return NULL; +} + +/* All options that affect the main loop and are not external. */ +#define MAIN_LOOP_OPTION_ENUMS \ + OPT_NO_WAIT, \ + OPT_WAIT, \ + OPT_PRINT_WAIT_TIME, \ + OPT_DRY_RUN, \ + OPT_ONELINE + +#define MAIN_LOOP_LONG_OPTIONS \ + {"no-wait", no_argument, NULL, OPT_NO_WAIT}, \ + {"wait", required_argument, NULL, OPT_WAIT}, \ + {"print-wait-time", no_argument, NULL, OPT_PRINT_WAIT_TIME}, \ + {"dry-run", no_argument, NULL, OPT_DRY_RUN}, \ + {"oneline", no_argument, NULL, OPT_ONELINE}, \ + {"timeout", required_argument, NULL, 't'} + +enum { + OPT_DB = UCHAR_MAX + 1, + OPT_NO_SYSLOG, + OPT_LOCAL, + OPT_COMMANDS, + OPT_OPTIONS, + OPT_LEADER_ONLY, + OPT_NO_LEADER_ONLY, + OPT_SHUFFLE_REMOTES, + OPT_NO_SHUFFLE_REMOTES, + OPT_BOOTSTRAP_CA_CERT, + MAIN_LOOP_OPTION_ENUMS, + OVN_DAEMON_OPTION_ENUMS, + VLOG_OPTION_ENUMS, + TABLE_OPTION_ENUMS, + SSL_OPTION_ENUMS, +}; + +static char * OVS_WARN_UNUSED_RESULT +handle_main_loop_option(int opt, const char *arg, bool *handled) +{ + ovs_assert(handled); + *handled = true; + + switch (opt) { + case OPT_ONELINE: + oneline = true; + break; + + case OPT_NO_WAIT: + wait_type = NBCTL_WAIT_NONE; + break; + + case OPT_WAIT: + if (!strcmp(arg, "none")) { + wait_type = NBCTL_WAIT_NONE; + } else if (!strcmp(arg, "sb")) { + wait_type = NBCTL_WAIT_SB; + } else if (!strcmp(arg, "hv")) { + wait_type = NBCTL_WAIT_HV; + } else { + return xstrdup("argument to --wait must be " + "\"none\", \"sb\", or \"hv\""); + } + break; + + case OPT_PRINT_WAIT_TIME: + print_wait_time = true; + break; + + case OPT_DRY_RUN: + dry_run = true; + break; + + case 't': + if (!str_to_uint(arg, 10, &timeout) || !timeout) { + return xasprintf("value %s on -t or --timeout is invalid", arg); + } + break; + + default: + *handled = false; + break; + } + + return NULL; +} + +static char * OVS_WARN_UNUSED_RESULT +build_short_options(const struct option *long_options, bool print_errors) +{ + char *tmp, *short_options; + + tmp = ovs_cmdl_long_options_to_short_options(long_options); + short_options = xasprintf("+%s%s", print_errors ? "" : ":", tmp); + free(tmp); + + return short_options; +} + +static struct option * OVS_WARN_UNUSED_RESULT +append_command_options(const struct option *options, int opt_val) +{ + struct option *o; + size_t n_allocated; + size_t n_existing; + int i; + + for (i = 0; options[i].name; i++) { + ; + } + n_allocated = i + 1; + n_existing = i; + + /* We want to parse both global and command-specific options here, but + * getopt_long() isn't too convenient for the job. We copy our global + * options into a dynamic array, then append all of the command-specific + * options. */ + o = xmemdup(options, n_allocated * sizeof *options); + ctl_add_cmd_options(&o, &n_existing, &n_allocated, opt_val); + + return o; +} + +static struct option * +get_all_options(void) +{ + static const struct option global_long_options[] = { + {"db", required_argument, NULL, OPT_DB}, + {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, + {"help", no_argument, NULL, 'h'}, + {"commands", no_argument, NULL, OPT_COMMANDS}, + {"options", no_argument, NULL, OPT_OPTIONS}, + {"leader-only", no_argument, NULL, OPT_LEADER_ONLY}, + {"no-leader-only", no_argument, NULL, OPT_NO_LEADER_ONLY}, + {"shuffle-remotes", no_argument, NULL, OPT_SHUFFLE_REMOTES}, + {"no-shuffle-remotes", no_argument, NULL, OPT_NO_SHUFFLE_REMOTES}, + {"version", no_argument, NULL, 'V'}, + {"unixctl", required_argument, NULL, 'u'}, + MAIN_LOOP_LONG_OPTIONS, + OVN_DAEMON_LONG_OPTIONS, + VLOG_LONG_OPTIONS, + STREAM_SSL_LONG_OPTIONS, + {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, + TABLE_LONG_OPTIONS, + {NULL, 0, NULL, 0}, + }; + + static struct option *options; + if (!options) { + options = append_command_options(global_long_options, OPT_LOCAL); + } + + return options; +} + +static bool +has_option(const struct ovs_cmdl_parsed_option *parsed_options, size_t n, + int option) +{ + for (const struct ovs_cmdl_parsed_option *po = parsed_options; + po < &parsed_options[n]; po++) { + if (po->o->val == option) { + return true; + } + } + return false; +} + +static bool +will_detach(const struct ovs_cmdl_parsed_option *parsed_options, size_t n) +{ + return has_option(parsed_options, n, OVN_OPT_DETACH); +} + +static char * OVS_WARN_UNUSED_RESULT +add_local_option(const char *name, const char *arg, + struct shash *local_options) +{ + char *full_name = xasprintf("--%s", name); + if (shash_find(local_options, full_name)) { + char *error = xasprintf("'%s' option specified multiple times", + full_name); + free(full_name); + return error; + } + shash_add_nocopy(local_options, full_name, nullable_xstrdup(arg)); + return NULL; +} + +static void +apply_options_direct(const struct ovn_dbctl_options *dbctl_options, + const struct ovs_cmdl_parsed_option *parsed_options, + size_t n, struct shash *local_options) +{ + for (const struct ovs_cmdl_parsed_option *po = parsed_options; + po < &parsed_options[n]; po++) { + bool handled; + char *error = handle_main_loop_option(po->o->val, po->arg, &handled); + if (error) { + ctl_fatal("%s", error); + } + if (handled) { + continue; + } + + optarg = po->arg; + switch (po->o->val) { + case OPT_DB: + db = po->arg; + break; + + case OPT_NO_SYSLOG: + vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN); + break; + + case OPT_LOCAL: + error = add_local_option(po->o->name, po->arg, local_options); + if (error) { + ctl_fatal("%s", error); + } + break; + + case 'h': + dbctl_options->usage(); + exit(EXIT_SUCCESS); + + case OPT_COMMANDS: + ctl_print_commands(); + /* fall through */ + + case OPT_OPTIONS: + ctl_print_options(get_all_options()); + /* fall through */ + + case OPT_LEADER_ONLY: + leader_only = true; + break; + + case OPT_NO_LEADER_ONLY: + leader_only = false; + break; + + case OPT_SHUFFLE_REMOTES: + shuffle_remotes = true; + break; + + case OPT_NO_SHUFFLE_REMOTES: + shuffle_remotes = false; + break; + + case 'u': + unixctl_path = optarg; + break; + + case 'V': + ovn_print_version(0, 0); + printf("DB Schema %s\n", dbctl_options->db_version); + exit(EXIT_SUCCESS); + + OVN_DAEMON_OPTION_HANDLERS + VLOG_OPTION_HANDLERS + TABLE_OPTION_HANDLERS(&table_style) + STREAM_SSL_OPTION_HANDLERS + + case OPT_BOOTSTRAP_CA_CERT: + stream_ssl_set_ca_cert_file(po->arg, true); + break; + + case '?': + exit(EXIT_FAILURE); + + default: + OVS_NOT_REACHED(); + + case 0: + break; + } + } + + if (!db) { + db = dbctl_options->default_db; + } +} + +static char * +run_prerequisites(const struct ovn_dbctl_options *dbctl_options, + struct ctl_command *commands, size_t n_commands, + struct ovsdb_idl *idl) +{ + dbctl_options->add_base_prerequisites(idl, wait_type); + + for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) { + if (c->syntax->prerequisites) { + struct ctl_context ctx; + + ds_init(&c->output); + c->table = NULL; + + ctl_context_init(&ctx, c, idl, NULL, NULL, NULL); + (c->syntax->prerequisites)(&ctx); + if (ctx.error) { + char *error = xstrdup(ctx.error); + ctl_context_done(&ctx, c); + return error; + } + ctl_context_done(&ctx, c); + + ovs_assert(!c->output.string); + ovs_assert(!c->table); + } + } + + return NULL; +} + +static void +oneline_format(struct ds *lines, struct ds *s) +{ + size_t j; + + ds_chomp(lines, '\n'); + for (j = 0; j < lines->length; j++) { + int ch = lines->string[j]; + switch (ch) { + case '\n': + ds_put_cstr(s, "\\n"); + break; + + case '\\': + ds_put_cstr(s, "\\\\"); + break; + + default: + ds_put_char(s, ch); + } + } + ds_put_char(s, '\n'); +} + +static void +oneline_print(struct ds *lines) +{ + struct ds s = DS_EMPTY_INITIALIZER; + oneline_format(lines, &s); + fputs(ds_cstr(&s), stdout); + ds_destroy(&s); +} + +static char * +do_dbctl(const struct ovn_dbctl_options *dbctl_options, + const char *args, struct ctl_command *commands, size_t n_commands, + struct ovsdb_idl *idl, const struct timer *wait_timeout, bool *retry) +{ + struct ovsdb_idl_txn *txn; + enum ovsdb_idl_txn_status status; + struct ovsdb_symbol_table *symtab; + struct ctl_command *c; + struct shash_node *node; + char *error = NULL; + + ovs_assert(retry); + + txn = the_idl_txn = ovsdb_idl_txn_create(idl); + if (dry_run) { + ovsdb_idl_txn_set_dry_run(txn); + } + + ovsdb_idl_txn_add_comment(txn, "%s: %s", program_name, args); + + dbctl_options->pre_execute(idl, txn, wait_type); + + symtab = ovsdb_symbol_table_create(); + for (c = commands; c < &commands[n_commands]; c++) { + ds_init(&c->output); + c->table = NULL; + } + struct ctl_context *ctx = dbctl_options->ctx_create(); + ctl_context_init(ctx, NULL, idl, txn, symtab, NULL); + for (c = commands; c < &commands[n_commands]; c++) { + ctl_context_init_command(ctx, c); + if (c->syntax->run) { + (c->syntax->run)(ctx); + } + if (ctx->error) { + error = xstrdup(ctx->error); + ctl_context_done(ctx, c); + goto out_error; + } + ctl_context_done_command(ctx, c); + + if (ctx->try_again) { + ctl_context_done(ctx, NULL); + goto try_again; + } + } + ctl_context_done(ctx, NULL); + + SHASH_FOR_EACH (node, &symtab->sh) { + struct ovsdb_symbol *symbol = node->data; + if (!symbol->created) { + error = xasprintf("row id \"%s\" is referenced but never created " + "(e.g. with \"-- --id=%s create ...\")", + node->name, node->name); + goto out_error; + } + if (!symbol->strong_ref) { + if (!symbol->weak_ref) { + VLOG_WARN("row id \"%s\" was created but no reference to it " + "was inserted, so it will not actually appear in " + "the database", node->name); + } else { + VLOG_WARN("row id \"%s\" was created but only a weak " + "reference to it was inserted, so it will not " + "actually appear in the database", node->name); + } + } + } + + long long int start_time = time_wall_msec(); + status = ovsdb_idl_txn_commit_block(txn); + if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { + for (c = commands; c < &commands[n_commands]; c++) { + if (c->syntax->postprocess) { + ctl_context_init(ctx, c, idl, txn, symtab, NULL); + (c->syntax->postprocess)(ctx); + if (ctx->error) { + error = xstrdup(ctx->error); + ctl_context_done(ctx, c); + goto out_error; + } + ctl_context_done(ctx, c); + } + } + } + + switch (status) { + case TXN_UNCOMMITTED: + case TXN_INCOMPLETE: + OVS_NOT_REACHED(); + + case TXN_ABORTED: + /* Should not happen--we never call ovsdb_idl_txn_abort(). */ + error = xstrdup("transaction aborted"); + goto out_error; + + case TXN_UNCHANGED: + case TXN_SUCCESS: + break; + + case TXN_TRY_AGAIN: + goto try_again; + + case TXN_ERROR: + error = xasprintf("transaction error: %s", + ovsdb_idl_txn_get_error(txn)); + goto out_error; + + case TXN_NOT_LOCKED: + /* Should not happen--we never call ovsdb_idl_set_lock(). */ + error = xstrdup("database not locked"); + goto out_error; + + default: + OVS_NOT_REACHED(); + } + + for (c = commands; c < &commands[n_commands]; c++) { + struct ds *ds = &c->output; + + if (c->table) { + table_print(c->table, &table_style); + } else if (oneline) { + oneline_print(ds); + } else { + fputs(ds_cstr(ds), stdout); + } + } + + if (dbctl_options->post_execute) { + error = dbctl_options->post_execute(idl, txn, status, wait_type, + wait_timeout, start_time, + print_wait_time); + if (error) { + goto out_error; + } + } + + dbctl_options->ctx_destroy(ctx); + ovsdb_symbol_table_destroy(symtab); + ovsdb_idl_txn_destroy(txn); + the_idl_txn = NULL; + + *retry = false; + return NULL; + +try_again: + /* Our transaction needs to be rerun, or a prerequisite was not met. Free + * resources and return so that the caller can try again. */ + *retry = true; + +out_error: + ovsdb_idl_txn_abort(txn); + ovsdb_idl_txn_destroy(txn); + the_idl_txn = NULL; + + dbctl_options->ctx_destroy(ctx); + ovsdb_symbol_table_destroy(symtab); + return error; +} + +/* Frees the current transaction and the underlying IDL and then calls + * exit(status). + * + * Freeing the transaction and the IDL is not strictly necessary, but it makes + * for a clean memory leak report from valgrind in the normal case. That makes + * it easier to notice real memory leaks. */ +static void +ovn_dbctl_exit(int status) +{ + if (the_idl_txn) { + ovsdb_idl_txn_abort(the_idl_txn); + ovsdb_idl_txn_destroy(the_idl_txn); + } + ovsdb_idl_destroy(the_idl); + exit(status); +} + +/* Server implementation. */ + +#undef ctl_fatal + +static const struct option * +find_option_by_value(const struct option *options, int value) +{ + const struct option *o; + + for (o = options; o->name; o++) { + if (o->val == value) { + return o; + } + } + return NULL; +} + +static char * OVS_WARN_UNUSED_RESULT +server_parse_options(int argc, char *argv[], struct shash *local_options, + int *n_options_p) +{ + static const struct option global_long_options[] = { + VLOG_LONG_OPTIONS, + MAIN_LOOP_LONG_OPTIONS, + TABLE_LONG_OPTIONS, + {NULL, 0, NULL, 0}, + }; + const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; + char *short_options; + struct option *options; + char *error = NULL; + + ovs_assert(n_options_p); + + short_options = build_short_options(global_long_options, false); + options = append_command_options(global_long_options, OPT_LOCAL); + + optind = 0; + opterr = 0; + for (;;) { + int idx; + int c; + + c = getopt_long(argc, argv, short_options, options, &idx); + if (c == -1) { + break; + } + + bool handled; + error = handle_main_loop_option(c, optarg, &handled); + if (error) { + goto out; + } + if (handled) { + continue; + } + + switch (c) { + case OPT_LOCAL: + error = add_local_option(options[idx].name, optarg, local_options); + if (error) { + goto out; + } + break; + + VLOG_OPTION_HANDLERS + TABLE_OPTION_HANDLERS(&table_style) + + case '?': + if (find_option_by_value(options, optopt)) { + error = xasprintf("option '%s' doesn't allow an argument", + argv[optind - 1]); + } else if (optopt) { + error = xasprintf("unrecognized option '%c'", optopt); + } else { + error = xasprintf("unrecognized option '%s'", argv[optind - 1]); + } + goto out; + break; + + case ':': + error = xasprintf("option '%s' requires an argument", + argv[optind - 1]); + goto out; + break; + + case 0: + break; + + default: + error = xasprintf("unhandled option '%c'", c); + goto out; + break; + } + } + *n_options_p = optind; + +out: + for (int i = n_global_long_options; options[i].name; i++) { + free(CONST_CAST(char *, options[i].name)); + } + free(options); + free(short_options); + + return error; +} + +static void +server_cmd_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, + const char *argv[] OVS_UNUSED, void *exiting_) +{ + bool *exiting = exiting_; + *exiting = true; + unixctl_command_reply(conn, NULL); +} + +struct server_cmd_run_ctx { + struct ovsdb_idl *idl; + const struct ovn_dbctl_options *dbctl_options; +}; + +static void +server_cmd_run(struct unixctl_conn *conn, int argc, const char **argv_, + void *ctx_) +{ + struct server_cmd_run_ctx *ctx = ctx_; + struct ovsdb_idl *idl = ctx->idl; + const struct ovn_dbctl_options *dbctl_options = ctx->dbctl_options; + + struct ctl_command *commands = NULL; + struct shash local_options; + size_t n_commands = 0; + int n_options = 0; + char *error = NULL; + + /* Copy args so that getopt() can permute them. Leave last entry NULL. */ + char **argv = xcalloc(argc + 1, sizeof *argv); + for (int i = 0; i < argc; i++) { + argv[i] = xstrdup(argv_[i]); + } + + /* Reset global state. */ + oneline = false; + dry_run = false; + wait_type = NBCTL_WAIT_NONE; + timeout = 0; + table_style = table_style_default; + + /* Parse commands & options. */ + char *args = process_escape_args(argv); + shash_init(&local_options); + error = server_parse_options(argc, argv, &local_options, &n_options); + if (error) { + unixctl_command_reply_error(conn, error); + goto out; + } + error = ctl_parse_commands(argc - n_options, argv + n_options, + &local_options, &commands, &n_commands); + if (error) { + unixctl_command_reply_error(conn, error); + goto out; + } + VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG, + "Running command %s", args); + + struct timer *wait_timeout = NULL; + struct timer wait_timeout_; + if (timeout) { + wait_timeout = &wait_timeout_; + timer_set_duration(wait_timeout, timeout * 1000); + } + + error = run_prerequisites(dbctl_options, commands, n_commands, idl); + if (error) { + unixctl_command_reply_error(conn, error); + goto out; + } + error = main_loop(dbctl_options, args, commands, n_commands, idl, wait_timeout); + if (error) { + unixctl_command_reply_error(conn, error); + goto out; + } + + struct ds output = DS_EMPTY_INITIALIZER; + table_format_reset(); + for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) { + if (c->table) { + table_format(c->table, &table_style, &output); + } else if (oneline) { + oneline_format(&c->output, &output); + } else { + ds_put_cstr(&output, ds_cstr_ro(&c->output)); + } + } + unixctl_command_reply(conn, ds_cstr_ro(&output)); + ds_destroy(&output); + +out: + free(error); + + struct ctl_command *c; + for (c = commands; c < &commands[n_commands]; c++) { + ds_destroy(&c->output); + table_destroy(c->table); + free(c->table); + shash_destroy_free_data(&c->options); + } + free(commands); + shash_destroy_free_data(&local_options); + free(args); + for (int i = 0; i < argc; i++) { + free(argv[i]); + } + free(argv); +} + +static void +server_loop(const struct ovn_dbctl_options *dbctl_options, + struct ovsdb_idl *idl, int argc, char *argv[]) +{ + struct unixctl_server *server = NULL; + bool exiting = false; + + service_start(&argc, &argv); + daemonize_start(false); + + char *abs_unixctl_path = get_abs_unix_ctl_path(unixctl_path); + int error = unixctl_server_create(abs_unixctl_path, &server); + free(abs_unixctl_path); + + if (error) { + ctl_fatal("failed to create unixctl server (%s)", + ovs_retval_to_string(error)); + } + puts(unixctl_server_get_path(server)); + fflush(stdout); + + struct server_cmd_run_ctx server_cmd_run_ctx = { + .idl = idl, + .dbctl_options = dbctl_options + }; + unixctl_command_register("run", "", 0, INT_MAX, server_cmd_run, + &server_cmd_run_ctx); + unixctl_command_register("exit", "", 0, 0, server_cmd_exit, &exiting); + + for (;;) { + memory_run(); + if (memory_should_report()) { + struct simap usage = SIMAP_INITIALIZER(&usage); + + /* Nothing special to report yet. */ + memory_report(&usage); + simap_destroy(&usage); + } + + ovsdb_idl_run(idl); + if (!ovsdb_idl_is_alive(idl)) { + int retval = ovsdb_idl_get_last_error(idl); + ctl_fatal("%s: database connection failed (%s)", + db, ovs_retval_to_string(retval)); + } + + if (ovsdb_idl_has_ever_connected(idl)) { + daemonize_complete(); + } + unixctl_server_run(server); + + if (exiting) { + break; + } + + memory_wait(); + ovsdb_idl_wait(idl); + unixctl_server_wait(server); + poll_block(); + } + + unixctl_server_destroy(server); +} + +static void +dbctl_client(const struct ovn_dbctl_options *dbctl_options, + const char *socket_name, + const struct ovs_cmdl_parsed_option *parsed_options, size_t n, + int argc, char *argv[]) +{ + struct svec args = SVEC_EMPTY_INITIALIZER; + + for (const struct ovs_cmdl_parsed_option *po = parsed_options; + po < &parsed_options[n]; po++) { + optarg = po->arg; + switch (po->o->val) { + case OPT_DB: + VLOG_WARN("not using %s daemon because of %s option", + program_name, po->o->name); + svec_destroy(&args); + return; + + case OPT_NO_SYSLOG: + vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN); + break; + + case 'h': + dbctl_options->usage(); + exit(EXIT_SUCCESS); + + case OPT_COMMANDS: + ctl_print_commands(); + /* fall through */ + + case OPT_OPTIONS: + ctl_print_options(get_all_options()); + /* fall through */ + + case OPT_LEADER_ONLY: + case OPT_NO_LEADER_ONLY: + case OPT_SHUFFLE_REMOTES: + case OPT_NO_SHUFFLE_REMOTES: + case OPT_BOOTSTRAP_CA_CERT: + STREAM_SSL_CASES + OVN_DAEMON_OPTION_CASES + VLOG_INFO("using %s daemon, ignoring %s option", + program_name, po->o->name); + break; + + case 'u': + socket_name = optarg; + break; + + case 'V': + ovs_print_version(0, 0); + printf("DB Schema %s\n", dbctl_options->db_version); + exit(EXIT_SUCCESS); + + case 't': + if (!str_to_uint(po->arg, 10, &timeout) || !timeout) { + ctl_fatal("value %s on -t or --timeout is invalid", po->arg); + } + break; + + VLOG_OPTION_HANDLERS + + case OPT_LOCAL: + default: + if (po->arg) { + svec_add_nocopy(&args, + xasprintf("--%s=%s", po->o->name, po->arg)); + } else { + svec_add_nocopy(&args, xasprintf("--%s", po->o->name)); + } + break; + } + } + + ovs_assert(socket_name && socket_name[0]); + + svec_add(&args, "--"); + for (int i = optind; i < argc; i++) { + svec_add(&args, argv[i]); + } + + ctl_timeout_setup(timeout); + + struct jsonrpc *client; + int error = unixctl_client_create(socket_name, &client); + if (error) { + ctl_fatal("%s: could not connect to %s daemon (%s); " + "unset %s to avoid using daemon", + socket_name, program_name, ovs_strerror(error), + dbctl_options->daemon_env_var_name); + } + + char *cmd_result; + char *cmd_error; + error = unixctl_client_transact(client, "run", + args.n, args.names, + &cmd_result, &cmd_error); + if (error) { + ctl_fatal("%s: transaction error (%s)", + socket_name, ovs_strerror(error)); + } + svec_destroy(&args); + + int exit_status; + if (cmd_error) { + exit_status = EXIT_FAILURE; + fprintf(stderr, "%s: %s", program_name, cmd_error); + } else { + exit_status = EXIT_SUCCESS; + fputs(cmd_result, stdout); + } + free(cmd_result); + free(cmd_error); + jsonrpc_close(client); + exit(exit_status); +} diff --git a/utilities/ovn-dbctl.h b/utilities/ovn-dbctl.h new file mode 100644 index 000000000000..5accf3c5e028 --- /dev/null +++ b/utilities/ovn-dbctl.h @@ -0,0 +1,60 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OVN_DBCTL_H +#define OVN_DBCTL_H 1 + +/* ovn-nbctl infrastructure code. */ + +#include <stdbool.h> +#include "ovsdb-idl.h" + +struct timer; + +enum nbctl_wait_type { + NBCTL_WAIT_NONE, /* Do not wait. */ + NBCTL_WAIT_SB, /* Wait for southbound database updates. */ + NBCTL_WAIT_HV /* Wait for hypervisors to catch up. */ +}; + +struct ovn_dbctl_options { + const char *db_version; /* Database schema version. */ + const char *default_db; /* Default database remote. */ + + /* Names of important environment variables. */ + const char *options_env_var_name; /* OVN_??_OPTIONS. */ + const char *daemon_env_var_name; /* OVN_??_DAEMON. */ + + const struct ovsdb_idl_class *idl_class; + const struct ctl_table_class *tables; + struct cmd_show_table *cmd_show_table; + const struct ctl_command_syntax *commands; + + void (*usage)(void); + + void (*add_base_prerequisites)(struct ovsdb_idl *, enum nbctl_wait_type); + void (*pre_execute)(struct ovsdb_idl *, struct ovsdb_idl_txn *, + enum nbctl_wait_type); + char *(*post_execute)(struct ovsdb_idl *, struct ovsdb_idl_txn *, + enum ovsdb_idl_txn_status, enum nbctl_wait_type, + const struct timer *wait_timeout, + long long int start_time, bool print_wait_time); + + struct ctl_context *(*ctx_create)(void); + void (*ctx_destroy)(struct ctl_context *); +}; + +int ovn_dbctl_main(int argc, char *argv[], const struct ovn_dbctl_options *); + +#endif /* ovn-dbctl.h */ diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c index e3af0be926da..14840a8fa074 100644 --- a/utilities/ovn-nbctl.c +++ b/utilities/ovn-nbctl.c @@ -30,6 +30,7 @@ #include "lib/ovn-nb-idl.h" #include "lib/ovn-util.h" #include "memory.h" +#include "ovn-dbctl.h" #include "packets.h" #include "openvswitch/poll-loop.h" #include "process.h" @@ -48,83 +49,109 @@ VLOG_DEFINE_THIS_MODULE(nbctl); -/* --db: The database server to contact. */ -static const char *db; +/* Should we wait (if specified by 'wait_type') even if the commands don't + * change the database at all? */ +static bool force_wait = false; -/* --oneline: Write each command's output as a single line? */ -static bool oneline; +static void +nbctl_add_base_prerequisites(struct ovsdb_idl *idl, + enum nbctl_wait_type wait_type) +{ + force_wait = false; -/* --dry-run: Do not commit any changes. */ -static bool dry_run; + ovsdb_idl_add_table(idl, &nbrec_table_nb_global); + if (wait_type == NBCTL_WAIT_SB) { + ovsdb_idl_add_column(idl, &nbrec_nb_global_col_sb_cfg); + ovsdb_idl_add_column(idl, &nbrec_nb_global_col_sb_cfg_timestamp); + } else if (wait_type == NBCTL_WAIT_HV) { + ovsdb_idl_add_column(idl, &nbrec_nb_global_col_hv_cfg); + ovsdb_idl_add_column(idl, &nbrec_nb_global_col_hv_cfg_timestamp); + } +} -/* --wait=TYPE: Wait for configuration change to take effect? */ -enum nbctl_wait_type { - NBCTL_WAIT_NONE, /* Do not wait. */ - NBCTL_WAIT_SB, /* Wait for southbound database updates. */ - NBCTL_WAIT_HV /* Wait for hypervisors to catch up. */ -}; -static enum nbctl_wait_type wait_type = NBCTL_WAIT_NONE; +static void +nbctl_pre_execute(struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn, + enum nbctl_wait_type wait_type) +{ + const struct nbrec_nb_global *nb = nbrec_nb_global_first(idl); + if (!nb) { + /* XXX add verification that table is empty */ + nb = nbrec_nb_global_insert(txn); + } -static bool print_wait_time = false; + /* Deal with potential overflows. */ + if (nb->nb_cfg == LLONG_MAX) { + nbrec_nb_global_set_nb_cfg(nb, 0); + } -/* Should we wait (if specified by 'wait_type') even if the commands don't - * change the database at all? */ -static bool force_wait = false; + if (wait_type != NBCTL_WAIT_NONE) { + ovsdb_idl_txn_increment(txn, &nb->header_, &nbrec_nb_global_col_nb_cfg, + force_wait); + } +} + +static char * +nbctl_post_execute(struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn, + enum ovsdb_idl_txn_status status, + enum nbctl_wait_type wait_type, + const struct timer *wait_timeout, long long int start_time, + bool print_wait_time) +{ + if (wait_type == NBCTL_WAIT_NONE) { + if (force_wait) { + VLOG_INFO("\"sync\" command has no effect without --wait"); + } + return NULL; + } + if (status == TXN_UNCHANGED) { + return NULL; + } + + ovs_assert(status == TXN_SUCCESS); + int64_t next_cfg = ovsdb_idl_txn_get_increment_new_value(txn); + ovsdb_idl_enable_reconnect(idl); + for (;;) { + ovsdb_idl_run(idl); + + const struct nbrec_nb_global *nb; + NBREC_NB_GLOBAL_FOR_EACH (nb, idl) { + int64_t cur_cfg = (wait_type == NBCTL_WAIT_SB + ? nb->sb_cfg + : MIN(nb->sb_cfg, nb->hv_cfg)); + if (cur_cfg >= next_cfg) { + if (print_wait_time) { + printf("Time spent on processing nb_cfg %"PRId64":\n", + next_cfg); + + long long int nb_timestamp = nb->nb_cfg_timestamp; + long long int sb_timestamp = nb->sb_cfg_timestamp; + long long int hv_timestamp = nb->hv_cfg_timestamp; + printf("\tovn-northd delay before processing:" + "\t%lldms\n", nb_timestamp - start_time); + printf("\tovn-northd completion:" + "\t\t\t%lldms\n", sb_timestamp - start_time); + if (wait_type == NBCTL_WAIT_HV) { + printf("\tovn-controller(s) completion:" + "\t\t%lldms\n", hv_timestamp - start_time); + } + } + return NULL; + } + } + ovsdb_idl_wait(idl); + if (wait_timeout) { + timer_wait(wait_timeout); + } + poll_block(); + if (wait_timeout && timer_expired(wait_timeout)) { + return xstrdup("timeout expired"); + } + } +} -/* --timeout: Time to wait for a connection to 'db'. */ -static unsigned int timeout; - -/* Format for table output. */ -static struct table_style table_style = TABLE_STYLE_DEFAULT; - -/* The IDL we're using and the current transaction, if any. - * This is for use by nbctl_exit() only, to allow it to clean up. - * Other code should use its context arguments. */ -static struct ovsdb_idl *the_idl; -static struct ovsdb_idl_txn *the_idl_txn; -OVS_NO_RETURN static void nbctl_exit(int status); - -/* --leader-only, --no-leader-only: Only accept the leader in a cluster. */ -static int leader_only = true; - -/* --shuffle-remotes, --no-shuffle-remotes: Shuffle the order of remotes that - * are specified in the connetion method string. */ -static int shuffle_remotes = true; - -/* --unixctl-path: Path to use for unixctl server socket, for daemon mode. */ -static char *unixctl_path; - -static unixctl_cb_func server_cmd_exit; -static unixctl_cb_func server_cmd_run; - -static void nbctl_cmd_init(void); -OVS_NO_RETURN static void usage(void); -static struct option *get_all_options(void); -static bool has_option(const struct ovs_cmdl_parsed_option *, size_t n, - int option); -static void nbctl_client(const char *socket_name, - const struct ovs_cmdl_parsed_option *, size_t n, - int argc, char *argv[]); -static bool will_detach(const struct ovs_cmdl_parsed_option *, size_t n); -static void apply_options_direct(const struct ovs_cmdl_parsed_option *, - size_t n, struct shash *local_options); -static char * OVS_WARN_UNUSED_RESULT run_prerequisites(struct ctl_command[], - size_t n_commands, - struct ovsdb_idl *); -static char * OVS_WARN_UNUSED_RESULT do_nbctl(const char *args, - struct ctl_command *, size_t n, - struct ovsdb_idl *, - const struct timer *, - bool *retry); static char * OVS_WARN_UNUSED_RESULT dhcp_options_get( struct ctl_context *ctx, const char *id, bool must_exist, const struct nbrec_dhcp_options **); -static char * OVS_WARN_UNUSED_RESULT main_loop(const char *args, - struct ctl_command *commands, - size_t n_commands, - struct ovsdb_idl *idl, - const struct timer *); -static void server_loop(struct ovsdb_idl *idl, int argc, char *argv[]); /* A context for keeping track of which switch/router certain ports are * connected to. @@ -134,35 +161,41 @@ static void server_loop(struct ovsdb_idl *idl, int argc, char *argv[]); * until transaction is committed and updates received from the server. */ struct nbctl_context { struct ctl_context base; + + bool context_valid; struct shash lsp_to_ls_map; struct shash lrp_to_lr_map; - bool context_valid; }; -static void -nbctl_context_init(struct nbctl_context *nbctx) +static struct ctl_context * +nbctl_ctx_create(void) { - nbctx->context_valid = false; - shash_init(&nbctx->lsp_to_ls_map); - shash_init(&nbctx->lrp_to_lr_map); + struct nbctl_context *nbctx = xmalloc(sizeof *nbctx); + *nbctx = (struct nbctl_context) { + .context_valid = false, + .lsp_to_ls_map = SHASH_INITIALIZER(&nbctx->lsp_to_ls_map), + .lrp_to_lr_map = SHASH_INITIALIZER(&nbctx->lrp_to_lr_map), + }; + return &nbctx->base; } static void -nbctl_context_destroy(struct nbctl_context *nbctx) +nbctl_ctx_destroy(struct ctl_context *base) { + struct nbctl_context *nbctx + = CONTAINER_OF(base, struct nbctl_context, base); nbctx->context_valid = false; shash_destroy(&nbctx->lsp_to_ls_map); shash_destroy(&nbctx->lrp_to_lr_map); + free(nbctx); } /* Casts 'base' into 'struct nbctl_context' and initializes it if needed. */ static struct nbctl_context * nbctl_context_get(struct ctl_context *base) { - struct nbctl_context *nbctx; - - nbctx = CONTAINER_OF(base, struct nbctl_context, base); - + struct nbctl_context *nbctx + = CONTAINER_OF(base, struct nbctl_context, base); if (nbctx->context_valid) { return nbctx; } @@ -185,466 +218,8 @@ nbctl_context_get(struct ctl_context *base) return nbctx; } -int -main(int argc, char *argv[]) -{ - struct ovsdb_idl *idl; - struct shash local_options; - - ovn_set_program_name(argv[0]); - fatal_ignore_sigpipe(); - vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN); - vlog_set_levels_from_string_assert("reconnect:warn"); - - nbctl_cmd_init(); - - /* Check if options are set via env var. */ - char **argv_ = ovs_cmdl_env_parse_all(&argc, argv, - getenv("OVN_NBCTL_OPTIONS")); - - /* ovn-nbctl has three operation modes: - * - * - Direct: Executes commands by contacting ovsdb-server directly. - * - * - Server: Runs in the background as a daemon waiting for requests - * from ovn-nbctl running in client mode. - * - * - Client: Executes commands by passing them to an ovn-nbctl running - * in the server mode. - * - * At this point we don't know what mode we're running in. The mode partly - * depends on the command line. So, for now we transform the command line - * into a parsed form, and figure out what to do with it later. - */ - struct ovs_cmdl_parsed_option *parsed_options; - size_t n_parsed_options; - char *error_s = ovs_cmdl_parse_all(argc, argv_, get_all_options(), - &parsed_options, &n_parsed_options); - if (error_s) { - ctl_fatal("%s", error_s); - } - - /* Now figure out the operation mode: - * - * - A --detach option implies server mode. - * - * - An OVN_NB_DAEMON environment variable implies client mode. - * - * - Otherwise, we're in direct mode. */ - char *socket_name = unixctl_path ?: getenv("OVN_NB_DAEMON"); - if (((socket_name && socket_name[0]) - || has_option(parsed_options, n_parsed_options, 'u')) - && !will_detach(parsed_options, n_parsed_options)) { - nbctl_client(socket_name, parsed_options, n_parsed_options, - argc, argv_); - } - - /* Parse command line. */ - shash_init(&local_options); - apply_options_direct(parsed_options, n_parsed_options, &local_options); - free(parsed_options); - - bool daemon_mode = false; - if (get_detach()) { - if (argc != optind) { - ctl_fatal("non-option arguments not supported with --detach " - "(use --help for help)"); - } - daemon_mode = true; - } - /* Initialize IDL. */ - idl = the_idl = ovsdb_idl_create_unconnected(&nbrec_idl_class, true); - ovsdb_idl_set_shuffle_remotes(idl, shuffle_remotes); - /* "retry" is true iff in daemon mode. */ - ovsdb_idl_set_remote(idl, db, daemon_mode); - ovsdb_idl_set_leader_only(idl, leader_only); - - if (daemon_mode) { - server_loop(idl, argc, argv_); - } else { - struct ctl_command *commands; - size_t n_commands; - char *error; - - error = ctl_parse_commands(argc - optind, argv_ + optind, - &local_options, &commands, &n_commands); - if (error) { - ctl_fatal("%s", error); - } - - char *args = process_escape_args(argv_); - VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG, - "Called as %s", args); - - ctl_timeout_setup(timeout); - - error = run_prerequisites(commands, n_commands, idl); - if (error) { - goto cleanup; - } - - error = main_loop(args, commands, n_commands, idl, NULL); - -cleanup: - free(args); - - struct ctl_command *c; - for (c = commands; c < &commands[n_commands]; c++) { - ds_destroy(&c->output); - table_destroy(c->table); - free(c->table); - shash_destroy_free_data(&c->options); - } - free(commands); - if (error) { - ctl_fatal("%s", error); - } - } - - ovsdb_idl_destroy(idl); - idl = the_idl = NULL; - - for (int i = 0; i < argc; i++) { - free(argv_[i]); - } - free(argv_); - exit(EXIT_SUCCESS); -} - -static char * -main_loop(const char *args, struct ctl_command *commands, size_t n_commands, - struct ovsdb_idl *idl, const struct timer *wait_timeout) -{ - unsigned int seqno; - bool idl_ready; - - /* Execute the commands. - * - * 'seqno' is the database sequence number for which we last tried to - * execute our transaction. There's no point in trying to commit more than - * once for any given sequence number, because if the transaction fails - * it's because the database changed and we need to obtain an up-to-date - * view of the database before we try the transaction again. */ - seqno = ovsdb_idl_get_seqno(idl); - - /* IDL might have already obtained the database copy during previous - * invocation. If so, we can't expect the sequence number to change before - * we issue any new requests. */ - idl_ready = ovsdb_idl_has_ever_connected(idl); - for (;;) { - ovsdb_idl_run(idl); - if (!ovsdb_idl_is_alive(idl)) { - int retval = ovsdb_idl_get_last_error(idl); - ctl_fatal("%s: database connection failed (%s)", - db, ovs_retval_to_string(retval)); - } - - if (idl_ready || seqno != ovsdb_idl_get_seqno(idl)) { - idl_ready = false; - seqno = ovsdb_idl_get_seqno(idl); - - bool retry; - char *error = do_nbctl(args, commands, n_commands, idl, - wait_timeout, &retry); - if (error) { - return error; - } - if (!retry) { - return NULL; - } - } - - if (seqno == ovsdb_idl_get_seqno(idl)) { - ovsdb_idl_wait(idl); - poll_block(); - } - } - - return NULL; -} - -/* All options that affect the main loop and are not external. */ -#define MAIN_LOOP_OPTION_ENUMS \ - OPT_NO_WAIT, \ - OPT_WAIT, \ - OPT_PRINT_WAIT_TIME, \ - OPT_DRY_RUN, \ - OPT_ONELINE - -#define MAIN_LOOP_LONG_OPTIONS \ - {"no-wait", no_argument, NULL, OPT_NO_WAIT}, \ - {"wait", required_argument, NULL, OPT_WAIT}, \ - {"print-wait-time", no_argument, NULL, OPT_PRINT_WAIT_TIME}, \ - {"dry-run", no_argument, NULL, OPT_DRY_RUN}, \ - {"oneline", no_argument, NULL, OPT_ONELINE}, \ - {"timeout", required_argument, NULL, 't'} - -enum { - OPT_DB = UCHAR_MAX + 1, - OPT_NO_SYSLOG, - OPT_LOCAL, - OPT_COMMANDS, - OPT_OPTIONS, - OPT_LEADER_ONLY, - OPT_NO_LEADER_ONLY, - OPT_SHUFFLE_REMOTES, - OPT_NO_SHUFFLE_REMOTES, - OPT_BOOTSTRAP_CA_CERT, - MAIN_LOOP_OPTION_ENUMS, - OVN_DAEMON_OPTION_ENUMS, - VLOG_OPTION_ENUMS, - TABLE_OPTION_ENUMS, - SSL_OPTION_ENUMS, -}; - -static char * OVS_WARN_UNUSED_RESULT -handle_main_loop_option(int opt, const char *arg, bool *handled) -{ - ovs_assert(handled); - *handled = true; - - switch (opt) { - case OPT_ONELINE: - oneline = true; - break; - - case OPT_NO_WAIT: - wait_type = NBCTL_WAIT_NONE; - break; - - case OPT_WAIT: - if (!strcmp(arg, "none")) { - wait_type = NBCTL_WAIT_NONE; - } else if (!strcmp(arg, "sb")) { - wait_type = NBCTL_WAIT_SB; - } else if (!strcmp(arg, "hv")) { - wait_type = NBCTL_WAIT_HV; - } else { - return xstrdup("argument to --wait must be " - "\"none\", \"sb\", or \"hv\""); - } - break; - - case OPT_PRINT_WAIT_TIME: - print_wait_time = true; - break; - - case OPT_DRY_RUN: - dry_run = true; - break; - - case 't': - if (!str_to_uint(arg, 10, &timeout) || !timeout) { - return xasprintf("value %s on -t or --timeout is invalid", arg); - } - break; - - default: - *handled = false; - break; - } - - return NULL; -} - -static char * OVS_WARN_UNUSED_RESULT -build_short_options(const struct option *long_options, bool print_errors) -{ - char *tmp, *short_options; - - tmp = ovs_cmdl_long_options_to_short_options(long_options); - short_options = xasprintf("+%s%s", print_errors ? "" : ":", tmp); - free(tmp); - - return short_options; -} - -static struct option * OVS_WARN_UNUSED_RESULT -append_command_options(const struct option *options, int opt_val) -{ - struct option *o; - size_t n_allocated; - size_t n_existing; - int i; - - for (i = 0; options[i].name; i++) { - ; - } - n_allocated = i + 1; - n_existing = i; - - /* We want to parse both global and command-specific options here, but - * getopt_long() isn't too convenient for the job. We copy our global - * options into a dynamic array, then append all of the command-specific - * options. */ - o = xmemdup(options, n_allocated * sizeof *options); - ctl_add_cmd_options(&o, &n_existing, &n_allocated, opt_val); - - return o; -} - -static struct option * -get_all_options(void) -{ - static const struct option global_long_options[] = { - {"db", required_argument, NULL, OPT_DB}, - {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG}, - {"help", no_argument, NULL, 'h'}, - {"commands", no_argument, NULL, OPT_COMMANDS}, - {"options", no_argument, NULL, OPT_OPTIONS}, - {"leader-only", no_argument, NULL, OPT_LEADER_ONLY}, - {"no-leader-only", no_argument, NULL, OPT_NO_LEADER_ONLY}, - {"shuffle-remotes", no_argument, NULL, OPT_SHUFFLE_REMOTES}, - {"no-shuffle-remotes", no_argument, NULL, OPT_NO_SHUFFLE_REMOTES}, - {"version", no_argument, NULL, 'V'}, - {"unixctl", required_argument, NULL, 'u'}, - MAIN_LOOP_LONG_OPTIONS, - OVN_DAEMON_LONG_OPTIONS, - VLOG_LONG_OPTIONS, - STREAM_SSL_LONG_OPTIONS, - {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, - TABLE_LONG_OPTIONS, - {NULL, 0, NULL, 0}, - }; - - static struct option *options; - if (!options) { - options = append_command_options(global_long_options, OPT_LOCAL); - } - - return options; -} - -static bool -has_option(const struct ovs_cmdl_parsed_option *parsed_options, size_t n, - int option) -{ - for (const struct ovs_cmdl_parsed_option *po = parsed_options; - po < &parsed_options[n]; po++) { - if (po->o->val == option) { - return true; - } - } - return false; -} - -static bool -will_detach(const struct ovs_cmdl_parsed_option *parsed_options, size_t n) -{ - return has_option(parsed_options, n, OVN_OPT_DETACH); -} - -static char * OVS_WARN_UNUSED_RESULT -add_local_option(const char *name, const char *arg, - struct shash *local_options) -{ - char *full_name = xasprintf("--%s", name); - if (shash_find(local_options, full_name)) { - char *error = xasprintf("'%s' option specified multiple times", - full_name); - free(full_name); - return error; - } - shash_add_nocopy(local_options, full_name, nullable_xstrdup(arg)); - return NULL; -} - -static void -apply_options_direct(const struct ovs_cmdl_parsed_option *parsed_options, - size_t n, struct shash *local_options) -{ - for (const struct ovs_cmdl_parsed_option *po = parsed_options; - po < &parsed_options[n]; po++) { - bool handled; - char *error = handle_main_loop_option(po->o->val, po->arg, &handled); - if (error) { - ctl_fatal("%s", error); - } - if (handled) { - continue; - } - - optarg = po->arg; - switch (po->o->val) { - case OPT_DB: - db = po->arg; - break; - - case OPT_NO_SYSLOG: - vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN); - break; - - case OPT_LOCAL: - error = add_local_option(po->o->name, po->arg, local_options); - if (error) { - ctl_fatal("%s", error); - } - break; - - case 'h': - usage(); - exit(EXIT_SUCCESS); - - case OPT_COMMANDS: - ctl_print_commands(); - /* fall through */ - - case OPT_OPTIONS: - ctl_print_options(get_all_options()); - /* fall through */ - - case OPT_LEADER_ONLY: - leader_only = true; - break; - - case OPT_NO_LEADER_ONLY: - leader_only = false; - break; - - case OPT_SHUFFLE_REMOTES: - shuffle_remotes = true; - break; - - case OPT_NO_SHUFFLE_REMOTES: - shuffle_remotes = false; - break; - - case 'u': - unixctl_path = optarg; - break; - - case 'V': - ovn_print_version(0, 0); - printf("DB Schema %s\n", nbrec_get_db_version()); - exit(EXIT_SUCCESS); - - OVN_DAEMON_OPTION_HANDLERS - VLOG_OPTION_HANDLERS - TABLE_OPTION_HANDLERS(&table_style) - STREAM_SSL_OPTION_HANDLERS - - case OPT_BOOTSTRAP_CA_CERT: - stream_ssl_set_ca_cert_file(po->arg, true); - break; - - case '?': - exit(EXIT_FAILURE); - - default: - abort(); - - case 0: - break; - } - } - - if (!db) { - db = default_nb_db(); - } -} - static void -usage(void) +nbctl_usage(void) { printf("\ %s: OVN northbound DB management utility\n\ @@ -1212,13 +787,9 @@ nbctl_init(struct ctl_context *ctx OVS_UNUSED) } static void -nbctl_pre_sync(struct ctl_context *ctx OVS_UNUSED) +nbctl_pre_sync(struct ctl_context *base OVS_UNUSED) { - if (wait_type != NBCTL_WAIT_NONE) { - force_wait = true; - } else { - VLOG_INFO("\"sync\" command has no effect without --wait"); - } + force_wait = true; } static void @@ -6208,305 +5779,6 @@ static const struct ctl_table_class tables[NBREC_N_TABLES] = { = {&nbrec_connection_col_target, NULL, NULL}, }; -static char * -run_prerequisites(struct ctl_command *commands, size_t n_commands, - struct ovsdb_idl *idl) -{ - ovsdb_idl_add_table(idl, &nbrec_table_nb_global); - if (wait_type == NBCTL_WAIT_SB) { - ovsdb_idl_add_column(idl, &nbrec_nb_global_col_sb_cfg); - ovsdb_idl_add_column(idl, &nbrec_nb_global_col_sb_cfg_timestamp); - } else if (wait_type == NBCTL_WAIT_HV) { - ovsdb_idl_add_column(idl, &nbrec_nb_global_col_hv_cfg); - ovsdb_idl_add_column(idl, &nbrec_nb_global_col_hv_cfg_timestamp); - } - - for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) { - if (c->syntax->prerequisites) { - struct ctl_context ctx; - - ds_init(&c->output); - c->table = NULL; - - ctl_context_init(&ctx, c, idl, NULL, NULL, NULL); - (c->syntax->prerequisites)(&ctx); - if (ctx.error) { - char *error = xstrdup(ctx.error); - ctl_context_done(&ctx, c); - return error; - } - ctl_context_done(&ctx, c); - - ovs_assert(!c->output.string); - ovs_assert(!c->table); - } - } - - return NULL; -} - -static void -oneline_format(struct ds *lines, struct ds *s) -{ - size_t j; - - ds_chomp(lines, '\n'); - for (j = 0; j < lines->length; j++) { - int ch = lines->string[j]; - switch (ch) { - case '\n': - ds_put_cstr(s, "\\n"); - break; - - case '\\': - ds_put_cstr(s, "\\\\"); - break; - - default: - ds_put_char(s, ch); - } - } - ds_put_char(s, '\n'); -} - -static void -oneline_print(struct ds *lines) -{ - struct ds s = DS_EMPTY_INITIALIZER; - oneline_format(lines, &s); - fputs(ds_cstr(&s), stdout); - ds_destroy(&s); -} - -static char * -do_nbctl(const char *args, struct ctl_command *commands, size_t n_commands, - struct ovsdb_idl *idl, const struct timer *wait_timeout, bool *retry) -{ - struct ovsdb_idl_txn *txn; - enum ovsdb_idl_txn_status status; - struct ovsdb_symbol_table *symtab; - struct nbctl_context ctx; - struct ctl_command *c; - struct shash_node *node; - int64_t next_cfg = 0; - char *error = NULL; - int64_t start_time = 0; - - ovs_assert(retry); - - txn = the_idl_txn = ovsdb_idl_txn_create(idl); - if (dry_run) { - ovsdb_idl_txn_set_dry_run(txn); - } - - ovsdb_idl_txn_add_comment(txn, "ovs-nbctl: %s", args); - - const struct nbrec_nb_global *nb = nbrec_nb_global_first(idl); - if (!nb) { - /* XXX add verification that table is empty */ - nb = nbrec_nb_global_insert(txn); - } - - /* Deal with potential overflows. */ - if (nb->nb_cfg == LLONG_MAX) { - nbrec_nb_global_set_nb_cfg(nb, 0); - } - - if (wait_type != NBCTL_WAIT_NONE) { - ovsdb_idl_txn_increment(txn, &nb->header_, &nbrec_nb_global_col_nb_cfg, - force_wait); - } - - symtab = ovsdb_symbol_table_create(); - for (c = commands; c < &commands[n_commands]; c++) { - ds_init(&c->output); - c->table = NULL; - } - nbctl_context_init(&ctx); - ctl_context_init(&ctx.base, NULL, idl, txn, symtab, NULL); - for (c = commands; c < &commands[n_commands]; c++) { - ctl_context_init_command(&ctx.base, c); - if (c->syntax->run) { - (c->syntax->run)(&ctx.base); - } - if (ctx.base.error) { - error = xstrdup(ctx.base.error); - ctl_context_done(&ctx.base, c); - goto out_error; - } - ctl_context_done_command(&ctx.base, c); - - if (ctx.base.try_again) { - ctl_context_done(&ctx.base, NULL); - goto try_again; - } - } - ctl_context_done(&ctx.base, NULL); - - SHASH_FOR_EACH (node, &symtab->sh) { - struct ovsdb_symbol *symbol = node->data; - if (!symbol->created) { - error = xasprintf("row id \"%s\" is referenced but never created " - "(e.g. with \"-- --id=%s create ...\")", - node->name, node->name); - goto out_error; - } - if (!symbol->strong_ref) { - if (!symbol->weak_ref) { - VLOG_WARN("row id \"%s\" was created but no reference to it " - "was inserted, so it will not actually appear in " - "the database", node->name); - } else { - VLOG_WARN("row id \"%s\" was created but only a weak " - "reference to it was inserted, so it will not " - "actually appear in the database", node->name); - } - } - } - - start_time = time_wall_msec(); - status = ovsdb_idl_txn_commit_block(txn); - if (wait_type != NBCTL_WAIT_NONE && status == TXN_SUCCESS) { - next_cfg = ovsdb_idl_txn_get_increment_new_value(txn); - } - if (status == TXN_UNCHANGED || status == TXN_SUCCESS) { - for (c = commands; c < &commands[n_commands]; c++) { - if (c->syntax->postprocess) { - ctl_context_init(&ctx.base, c, idl, txn, symtab, NULL); - (c->syntax->postprocess)(&ctx.base); - if (ctx.base.error) { - error = xstrdup(ctx.base.error); - ctl_context_done(&ctx.base, c); - goto out_error; - } - ctl_context_done(&ctx.base, c); - } - } - } - - switch (status) { - case TXN_UNCOMMITTED: - case TXN_INCOMPLETE: - OVS_NOT_REACHED(); - - case TXN_ABORTED: - /* Should not happen--we never call ovsdb_idl_txn_abort(). */ - error = xstrdup("transaction aborted"); - goto out_error; - - case TXN_UNCHANGED: - case TXN_SUCCESS: - break; - - case TXN_TRY_AGAIN: - goto try_again; - - case TXN_ERROR: - error = xasprintf("transaction error: %s", - ovsdb_idl_txn_get_error(txn)); - goto out_error; - - case TXN_NOT_LOCKED: - /* Should not happen--we never call ovsdb_idl_set_lock(). */ - error = xstrdup("database not locked"); - goto out_error; - - default: - OVS_NOT_REACHED(); - } - - for (c = commands; c < &commands[n_commands]; c++) { - struct ds *ds = &c->output; - - if (c->table) { - table_print(c->table, &table_style); - } else if (oneline) { - oneline_print(ds); - } else { - fputs(ds_cstr(ds), stdout); - } - } - - if (wait_type != NBCTL_WAIT_NONE && status != TXN_UNCHANGED) { - ovsdb_idl_enable_reconnect(idl); - for (;;) { - ovsdb_idl_run(idl); - NBREC_NB_GLOBAL_FOR_EACH (nb, idl) { - int64_t cur_cfg = (wait_type == NBCTL_WAIT_SB - ? nb->sb_cfg - : MIN(nb->sb_cfg, nb->hv_cfg)); - if (cur_cfg >= next_cfg) { - if (print_wait_time) { - printf("Time spent on processing nb_cfg %"PRId64":\n", - next_cfg); - printf("\tovn-northd delay before processing:" - "\t%"PRId64"ms\n", - nb->nb_cfg_timestamp - start_time); - printf("\tovn-northd completion:" - "\t\t\t%"PRId64"ms\n", - nb->sb_cfg_timestamp - start_time); - if (wait_type == NBCTL_WAIT_HV) { - printf("\tovn-controller(s) completion:" - "\t\t%"PRId64"ms\n", - nb->hv_cfg_timestamp - start_time); - } - } - goto done; - } - } - ovsdb_idl_wait(idl); - if (wait_timeout) { - timer_wait(wait_timeout); - } - poll_block(); - if (wait_timeout && timer_expired(wait_timeout)) { - error = xstrdup("timeout expired"); - goto out_error; - } - } - done: ; - } - - nbctl_context_destroy(&ctx); - ovsdb_symbol_table_destroy(symtab); - ovsdb_idl_txn_destroy(txn); - the_idl_txn = NULL; - - *retry = false; - return NULL; - -try_again: - /* Our transaction needs to be rerun, or a prerequisite was not met. Free - * resources and return so that the caller can try again. */ - *retry = true; - -out_error: - ovsdb_idl_txn_abort(txn); - ovsdb_idl_txn_destroy(txn); - the_idl_txn = NULL; - - nbctl_context_destroy(&ctx); - ovsdb_symbol_table_destroy(symtab); - return error; -} - -/* Frees the current transaction and the underlying IDL and then calls - * exit(status). - * - * Freeing the transaction and the IDL is not strictly necessary, but it makes - * for a clean memory leak report from valgrind in the normal case. That makes - * it easier to notice real memory leaks. */ -static void -nbctl_exit(int status) -{ - if (the_idl_txn) { - ovsdb_idl_txn_abort(the_idl_txn); - ovsdb_idl_txn_destroy(the_idl_txn); - } - ovsdb_idl_destroy(the_idl); - exit(status); -} - static const struct ctl_command_syntax nbctl_commands[] = { { "init", 0, 0, "", NULL, nbctl_init, NULL, "", RW }, { "sync", 0, 0, "", nbctl_pre_sync, nbctl_sync, NULL, "", RO }, @@ -6708,401 +5980,29 @@ static const struct ctl_command_syntax nbctl_commands[] = { {NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO}, }; -/* Registers nbctl and common db commands. */ -static void -nbctl_cmd_init(void) +int +main(int argc, char *argv[]) { - ctl_init(&nbrec_idl_class, nbrec_table_classes, tables, NULL, nbctl_exit); - ctl_register_commands(nbctl_commands); -} - -/* Server implementation. */ + struct ovn_dbctl_options dbctl_options = { + .db_version = nbrec_get_db_version(), + .default_db = default_nb_db(), -#undef ctl_fatal + .options_env_var_name = "OVN_NBCTL_OPTIONS", + .daemon_env_var_name = "OVN_NB_DAEMON", -static const struct option * -find_option_by_value(const struct option *options, int value) -{ - const struct option *o; + .idl_class = &nbrec_idl_class, + .tables = tables, + .cmd_show_table = NULL, + .commands = nbctl_commands, - for (o = options; o->name; o++) { - if (o->val == value) { - return o; - } - } - return NULL; -} + .usage = nbctl_usage, + .add_base_prerequisites = nbctl_add_base_prerequisites, + .pre_execute = nbctl_pre_execute, + .post_execute = nbctl_post_execute, -static char * OVS_WARN_UNUSED_RESULT -server_parse_options(int argc, char *argv[], struct shash *local_options, - int *n_options_p) -{ - static const struct option global_long_options[] = { - VLOG_LONG_OPTIONS, - MAIN_LOOP_LONG_OPTIONS, - TABLE_LONG_OPTIONS, - {NULL, 0, NULL, 0}, + .ctx_create = nbctl_ctx_create, + .ctx_destroy = nbctl_ctx_destroy, }; - const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1; - char *short_options; - struct option *options; - char *error = NULL; - - ovs_assert(n_options_p); - - short_options = build_short_options(global_long_options, false); - options = append_command_options(global_long_options, OPT_LOCAL); - - optind = 0; - opterr = 0; - for (;;) { - int idx; - int c; - - c = getopt_long(argc, argv, short_options, options, &idx); - if (c == -1) { - break; - } - - bool handled; - error = handle_main_loop_option(c, optarg, &handled); - if (error) { - goto out; - } - if (handled) { - continue; - } - - switch (c) { - case OPT_LOCAL: - error = add_local_option(options[idx].name, optarg, local_options); - if (error) { - goto out; - } - break; - - VLOG_OPTION_HANDLERS - TABLE_OPTION_HANDLERS(&table_style) - - case '?': - if (find_option_by_value(options, optopt)) { - error = xasprintf("option '%s' doesn't allow an argument", - argv[optind-1]); - } else if (optopt) { - error = xasprintf("unrecognized option '%c'", optopt); - } else { - error = xasprintf("unrecognized option '%s'", argv[optind-1]); - } - goto out; - break; - - case ':': - error = xasprintf("option '%s' requires an argument", - argv[optind-1]); - goto out; - break; - - case 0: - break; - - default: - error = xasprintf("unhandled option '%c'", c); - goto out; - break; - } - } - *n_options_p = optind; - -out: - for (int i = n_global_long_options; options[i].name; i++) { - free(CONST_CAST(char *, options[i].name)); - } - free(options); - free(short_options); - - return error; -} - -static void -server_cmd_exit(struct unixctl_conn *conn, int argc OVS_UNUSED, - const char *argv[] OVS_UNUSED, void *exiting_) -{ - bool *exiting = exiting_; - *exiting = true; - unixctl_command_reply(conn, NULL); -} - -static void -server_cmd_run(struct unixctl_conn *conn, int argc, const char **argv_, - void *idl_) -{ - struct ovsdb_idl *idl = idl_; - struct ctl_command *commands = NULL; - struct shash local_options; - size_t n_commands = 0; - int n_options = 0; - char *error = NULL; - - /* Copy args so that getopt() can permute them. Leave last entry NULL. */ - char **argv = xcalloc(argc + 1, sizeof *argv); - for (int i = 0; i < argc; i++) { - argv[i] = xstrdup(argv_[i]); - } - - /* Reset global state. */ - oneline = false; - dry_run = false; - wait_type = NBCTL_WAIT_NONE; - force_wait = false; - timeout = 0; - table_style = table_style_default; - - /* Parse commands & options. */ - char *args = process_escape_args(argv); - shash_init(&local_options); - error = server_parse_options(argc, argv, &local_options, &n_options); - if (error) { - unixctl_command_reply_error(conn, error); - goto out; - } - error = ctl_parse_commands(argc - n_options, argv + n_options, - &local_options, &commands, &n_commands); - if (error) { - unixctl_command_reply_error(conn, error); - goto out; - } - VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG, - "Running command %s", args); - - struct timer *wait_timeout = NULL; - struct timer wait_timeout_; - if (timeout) { - wait_timeout = &wait_timeout_; - timer_set_duration(wait_timeout, timeout * 1000); - } - - error = run_prerequisites(commands, n_commands, idl); - if (error) { - unixctl_command_reply_error(conn, error); - goto out; - } - error = main_loop(args, commands, n_commands, idl, wait_timeout); - if (error) { - unixctl_command_reply_error(conn, error); - goto out; - } - - struct ds output = DS_EMPTY_INITIALIZER; - table_format_reset(); - for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) { - if (c->table) { - table_format(c->table, &table_style, &output); - } else if (oneline) { - oneline_format(&c->output, &output); - } else { - ds_put_cstr(&output, ds_cstr_ro(&c->output)); - } - } - unixctl_command_reply(conn, ds_cstr_ro(&output)); - ds_destroy(&output); - -out: - free(error); - - struct ctl_command *c; - for (c = commands; c < &commands[n_commands]; c++) { - ds_destroy(&c->output); - table_destroy(c->table); - free(c->table); - shash_destroy_free_data(&c->options); - } - free(commands); - shash_destroy_free_data(&local_options); - free(args); - for (int i = 0; i < argc; i++) { - free(argv[i]); - } - free(argv); -} - -static void -server_cmd_init(struct ovsdb_idl *idl, bool *exiting) -{ - unixctl_command_register("exit", "", 0, 0, server_cmd_exit, exiting); - unixctl_command_register("run", "", 0, INT_MAX, server_cmd_run, idl); -} - -static void -server_loop(struct ovsdb_idl *idl, int argc, char *argv[]) -{ - struct unixctl_server *server = NULL; - bool exiting = false; - - service_start(&argc, &argv); - daemonize_start(false); - - char *abs_unixctl_path = get_abs_unix_ctl_path(unixctl_path); - int error = unixctl_server_create(abs_unixctl_path, &server); - free(abs_unixctl_path); - - if (error) { - ctl_fatal("failed to create unixctl server (%s)", - ovs_retval_to_string(error)); - } - puts(unixctl_server_get_path(server)); - fflush(stdout); - server_cmd_init(idl, &exiting); - - for (;;) { - memory_run(); - if (memory_should_report()) { - struct simap usage = SIMAP_INITIALIZER(&usage); - - /* Nothing special to report yet. */ - memory_report(&usage); - simap_destroy(&usage); - } - - ovsdb_idl_run(idl); - if (!ovsdb_idl_is_alive(idl)) { - int retval = ovsdb_idl_get_last_error(idl); - ctl_fatal("%s: database connection failed (%s)", - db, ovs_retval_to_string(retval)); - } - - if (ovsdb_idl_has_ever_connected(idl)) { - daemonize_complete(); - } - unixctl_server_run(server); - - if (exiting) { - break; - } - - memory_wait(); - ovsdb_idl_wait(idl); - unixctl_server_wait(server); - poll_block(); - } - - unixctl_server_destroy(server); -} - -static void -nbctl_client(const char *socket_name, - const struct ovs_cmdl_parsed_option *parsed_options, size_t n, - int argc, char *argv[]) -{ - struct svec args = SVEC_EMPTY_INITIALIZER; - - for (const struct ovs_cmdl_parsed_option *po = parsed_options; - po < &parsed_options[n]; po++) { - optarg = po->arg; - switch (po->o->val) { - case OPT_DB: - VLOG_WARN("not using ovn-nbctl daemon because of %s option", - po->o->name); - svec_destroy(&args); - return; - - case OPT_NO_SYSLOG: - vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN); - break; - - case 'h': - usage(); - exit(EXIT_SUCCESS); - - case OPT_COMMANDS: - ctl_print_commands(); - /* fall through */ - - case OPT_OPTIONS: - ctl_print_options(get_all_options()); - /* fall through */ - - case OPT_LEADER_ONLY: - case OPT_NO_LEADER_ONLY: - case OPT_SHUFFLE_REMOTES: - case OPT_NO_SHUFFLE_REMOTES: - case OPT_BOOTSTRAP_CA_CERT: - STREAM_SSL_CASES - OVN_DAEMON_OPTION_CASES - VLOG_INFO("using ovn-nbctl daemon, ignoring %s option", - po->o->name); - break; - - case 'u': - socket_name = optarg; - break; - - case 'V': - ovs_print_version(0, 0); - printf("DB Schema %s\n", nbrec_get_db_version()); - exit(EXIT_SUCCESS); - case 't': - if (!str_to_uint(po->arg, 10, &timeout) || !timeout) { - ctl_fatal("value %s on -t or --timeout is invalid", po->arg); - } - break; - - VLOG_OPTION_HANDLERS - - case OPT_LOCAL: - default: - if (po->arg) { - svec_add_nocopy(&args, - xasprintf("--%s=%s", po->o->name, po->arg)); - } else { - svec_add_nocopy(&args, xasprintf("--%s", po->o->name)); - } - break; - } - } - - ovs_assert(socket_name && socket_name[0]); - - svec_add(&args, "--"); - for (int i = optind; i < argc; i++) { - svec_add(&args, argv[i]); - } - - ctl_timeout_setup(timeout); - - struct jsonrpc *client; - int error = unixctl_client_create(socket_name, &client); - if (error) { - ctl_fatal("%s: could not connect to ovn-nb daemon (%s); " - "unset OVN_NB_DAEMON to avoid using daemon", - socket_name, ovs_strerror(error)); - } - - char *cmd_result; - char *cmd_error; - error = unixctl_client_transact(client, "run", - args.n, args.names, - &cmd_result, &cmd_error); - if (error) { - ctl_fatal("%s: transaction error (%s)", - socket_name, ovs_strerror(error)); - } - svec_destroy(&args); - - int exit_status; - if (cmd_error) { - exit_status = EXIT_FAILURE; - fprintf(stderr, "%s: %s", program_name, cmd_error); - } else { - exit_status = EXIT_SUCCESS; - fputs(cmd_result, stdout); - } - free(cmd_result); - free(cmd_error); - jsonrpc_close(client); - for (int i = 0; i < argc; i++) { - free(argv[i]); - } - free(argv); - exit(exit_status); + return ovn_dbctl_main(argc, argv, &dbctl_options); } -- 2.31.1 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
