[ovs-dev] [PATCH v2 06/26] ovn-nbctl: Refactor into infrastructure and northbound details.
Ben Pfaff
blp at ovn.org
Thu Apr 1 23:20:48 UTC 2021
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 <blp at ovn.org>
---
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 38a4cc7dbd57..42841bfb8890 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
@@ -6204,305 +5775,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 },
@@ -6704,401 +5976,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.29.2
More information about the dev
mailing list