From patchwork Fri Jul 13 18:36:22 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Sitnicki X-Patchwork-Id: 943780 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 41S1sS0lKWz9s0n for ; Sat, 14 Jul 2018 04:43:24 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id D5476E80; Fri, 13 Jul 2018 18:36:55 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id 1325EE74 for ; Fri, 13 Jul 2018 18:36:54 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 974A17B2 for ; Fri, 13 Jul 2018 18:36:52 +0000 (UTC) Received: by mail-wr1-f43.google.com with SMTP id m1-v6so13284098wrg.5 for ; Fri, 13 Jul 2018 11:36:52 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=0K+mbDRwuFnU1zU8nuDPjWh4GPHRy1sqKqMNSeCr41o=; b=ExT/mSjb9zqq9JkslzaIm4MI3M8MkRdgffD3JwgWmbpTvSMd1QDs55xK/Hl5iHWmao F23KLMbv/wTQ20l74HM18tjwrUXqLPFzKhHYwsIQHaoX3p+pSqm8ZUWGtCwG1Rlm+YHF 6eE5DApuVqy0hmGxT30Ld8gNzvdsefalsitzFfOybcB3y6C7eRkuG3VDekdau2hLh3fK NoNlEfwrkFVVJ2hyOCDEnyvK36y7xGK4crwDmzLg9gaaLXPwfAmbc1zU7et0EAHXy8ix tarX/Nh55DJ5enYy2NaASCpeh7qW/Haw/Kz7NHc+OLCPab16sWxfCkQw8mtWNWvu8TTw sETA== X-Gm-Message-State: AOUpUlFC9IYhxllX5JG3J53Ti0QpqFFMA9BGw8qzXlxkXYIKrpRkWMr5 gLfXa3L1fT5hIB7eAyGjx87SCi13NRc= X-Google-Smtp-Source: AAOMgpehS3dUJNDpGGVxY4aRa2wnD4EE6PDumkTQ9dvwB6KxoCgCxiGdUxgg2RivQoLF5luebaTl1w== X-Received: by 2002:adf:96c3:: with SMTP id u61-v6mr5587302wrb.72.1531507010759; Fri, 13 Jul 2018 11:36:50 -0700 (PDT) Received: from redhat.com (218.red-2-136-166.dynamicip.rima-tde.net. [2.136.166.218]) by smtp.gmail.com with ESMTPSA id b6-v6sm55671129wru.66.2018.07.13.11.36.49 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 13 Jul 2018 11:36:50 -0700 (PDT) From: Jakub Sitnicki To: dev@openvswitch.org Date: Fri, 13 Jul 2018 20:36:22 +0200 Message-Id: <20180713183624.16937-16-jkbs@redhat.com> X-Mailer: git-send-email 2.14.4 In-Reply-To: <20180713183624.16937-1-jkbs@redhat.com> References: <20180713183624.16937-1-jkbs@redhat.com> X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v3 15/17] ovn-nbctl: Initial support for daemon mode. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Make ovn-nbctl act as a unixctl server if we were asked to detach. This turns ovn-nbctl into a long-lived process that acts a proxy for interacting with NB DB. The main difference to regular mode of ovn-nbctl is that in the daemon mode, a local copy of database contents has to be obtained only once. Just two unixctl commands are supported 'run' and 'exit'. The former can be used to run any ovn-nbctl command or a batch of them as so: ovs-appctl -t ovn-nbctl run [OPTIONS] COMMAND [-- [OPTIONS] COMMAND] ... Running commands that have not yet been converted to not use ctl_fatal() will result in death of the daemon process. However, --monitor option can be used to keep the daemon running. Signed-off-by: Jakub Sitnicki --- ovn/utilities/ovn-nbctl.8.xml | 40 ++++++ ovn/utilities/ovn-nbctl.c | 308 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 322 insertions(+), 26 deletions(-) diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml index abba4ecdb..2cd2fab30 100644 --- a/ovn/utilities/ovn-nbctl.8.xml +++ b/ovn/utilities/ovn-nbctl.8.xml @@ -913,6 +913,43 @@ +

Daemon Mode

+ +

+ If ovn-nbctl is invoked with the --detach + option (see Daemon Options, below), it runs in the + background as a daemon and accepts commands from ovs-appctl + (or another JSON-RPC client) indefinitely. The currently supported + commands are described below. +

+ +

+ +

+ +
+
+ run [options] command + [arg...] [-- [options] + command [arg...] ...] +
+
+ Instructs the daemon process to run one or more ovn-nbctl + commands described above and reply with the results of running these + commands. Accepts the --no-wait, --wait, + --timeout, --dry-run, --oneline, + and the options described under Table Formatting Options + in addition to the the command-specific options. +
+ +
exit
+
Causes ovn-nbctl to gracefully terminate.
+
+ +

+ Daemon mode is considered experimental. +

+

Options

@@ -982,6 +1019,9 @@
+

Daemon Options

+ +

Logging options

diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index c83d03218..46e923cb9 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -20,6 +20,7 @@ #include #include "command-line.h" +#include "daemon.h" #include "db-ctl-base.h" #include "dirs.h" #include "fatal-signal.h" @@ -38,6 +39,7 @@ #include "table.h" #include "timeval.h" #include "timer.h" +#include "unixctl.h" #include "util.h" #include "openvswitch/vlog.h" @@ -80,6 +82,13 @@ 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; +/* --unixctl-path: Path to use for unixctl server, for "monitor" and "snoop" + commands. */ +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 void parse_options(int argc, char *argv[], struct shash *local_options); @@ -98,15 +107,13 @@ static char * OVS_WARN_UNUSED_RESULT main_loop(const char *args, size_t n_commands, struct ovsdb_idl *idl, const struct timer *); +static void server_loop(struct ovsdb_idl *idl); int main(int argc, char *argv[]) { struct ovsdb_idl *idl; - struct ctl_command *commands; struct shash local_options; - size_t n_commands; - char *error; set_program_name(argv[0]); fatal_ignore_sigpipe(); @@ -119,38 +126,55 @@ main(int argc, char *argv[]) char *args = process_escape_args(argv); shash_init(&local_options); parse_options(argc, argv, &local_options); - commands = ctl_parse_commands(argc - optind, argv + optind, &local_options, - &n_commands); - VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG, - "Called as %s", args); - - if (timeout) { - time_alarm(timeout); - } + argc -= optind; + argv += optind; /* Initialize IDL. */ idl = the_idl = ovsdb_idl_create(db, &nbrec_idl_class, true, false); ovsdb_idl_set_leader_only(idl, leader_only); - error = run_prerequisites(commands, n_commands, idl); - if (error) { - ctl_fatal("%s", error); - } - error = main_loop(args, commands, n_commands, idl, NULL); - if (error) { - ctl_fatal("%s", error); + if (get_detach()) { + if (argc != 0) { + ctl_fatal("non-option arguments not supported with --detach " + "(use --help for help)"); + } + server_loop(idl); + } else { + struct ctl_command *commands; + size_t n_commands; + char *error; + + commands = ctl_parse_commands(argc, argv, &local_options, &n_commands); + VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG, + "Called as %s", args); + + if (timeout) { + time_alarm(timeout); + } + + error = run_prerequisites(commands, n_commands, idl); + if (error) { + ctl_fatal("%s", error); + } + + error = main_loop(args, commands, n_commands, idl, NULL); + if (error) { + ctl_fatal("%s", 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); } ovsdb_idl_destroy(idl); idl = the_idl = NULL; - for (struct ctl_command *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); free(args); exit(EXIT_SUCCESS); } @@ -160,6 +184,7 @@ 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. * @@ -169,6 +194,11 @@ main_loop(const char *args, struct ctl_command *commands, size_t n_commands, * 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)) { @@ -177,7 +207,8 @@ main_loop(const char *args, struct ctl_command *commands, size_t n_commands, db, ovs_retval_to_string(retval)); } - if (seqno != ovsdb_idl_get_seqno(idl)) { + if (idl_ready || seqno != ovsdb_idl_get_seqno(idl)) { + idl_ready = false; seqno = ovsdb_idl_get_seqno(idl); bool retry; @@ -222,6 +253,7 @@ enum { OPT_OPTIONS, OPT_BOOTSTRAP_CA_CERT, MAIN_LOOP_OPTION_ENUMS, + DAEMON_OPTION_ENUMS, VLOG_OPTION_ENUMS, TABLE_OPTION_ENUMS, SSL_OPTION_ENUMS, @@ -323,6 +355,7 @@ parse_options(int argc, char *argv[], struct shash *local_options) {"no-leader-only", no_argument, &leader_only, false}, {"version", no_argument, NULL, 'V'}, MAIN_LOOP_LONG_OPTIONS, + DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, @@ -391,6 +424,7 @@ parse_options(int argc, char *argv[], struct shash *local_options) printf("DB Schema %s\n", nbrec_get_db_version()); exit(EXIT_SUCCESS); + DAEMON_OPTION_HANDLERS VLOG_OPTION_HANDLERS TABLE_OPTION_HANDLERS(&table_style) STREAM_SSL_OPTION_HANDLERS @@ -584,6 +618,7 @@ Options:\n\ program_name, program_name, ctl_get_db_cmd_usage(), ctl_list_db_tables_usage(), default_nb_db()); table_usage(); + daemon_usage(); vlog_usage(); printf("\ --no-syslog equivalent to --verbose=nbctl:syslog:warn\n"); @@ -4617,3 +4652,224 @@ nbctl_cmd_init(void) ctl_init(&nbrec_idl_class, nbrec_table_classes, tables, NULL, nbctl_exit); ctl_register_commands(nbctl_commands); } + +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[] = { + 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); + 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: + if (shash_find(local_options, options[idx].name)) { + error = xasprintf("'%s' option specified multiple times", + options[idx].name); + goto out; + } + shash_add_nocopy(local_options, + xasprintf("--%s", options[idx].name), + nullable_xstrdup(optarg)); + break; + + VLOG_OPTION_HANDLERS + TABLE_OPTION_HANDLERS(&table_style) + + case '?': + error = xstrdup("Invalid option or argument"); + 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; + } + commands = ctl_parse_commands(argc - n_options, argv + n_options, + &local_options, &n_commands); + 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; + 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)); + } + + ds_destroy(&c->output); + table_destroy(c->table); + free(c->table); + } + unixctl_command_reply(conn, ds_cstr_ro(&output)); + ds_destroy(&output); + +out: + free(error); + for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) { + 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", "", 1, INT_MAX, server_cmd_run, idl); +} + +static void +server_loop(struct ovsdb_idl *idl) +{ + struct unixctl_server *server = NULL; + bool exiting = false; + + daemonize_start(false); + int error = unixctl_server_create(unixctl_path, &server); + if (error) { + ctl_fatal("failed to create unixctl server (%s)", + ovs_retval_to_string(error)); + } + server_cmd_init(idl, &exiting); + + 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 (ovsdb_idl_has_ever_connected(idl)) { + daemonize_complete(); + unixctl_server_run(server); + } + if (exiting) { + break; + } + + ovsdb_idl_wait(idl); + unixctl_server_wait(server); + poll_block(); + } + + unixctl_server_destroy(server); +}