From patchwork Thu Jul 12 13:40:13 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Sitnicki X-Patchwork-Id: 943056 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 41RHKT4Pjyz9s3R for ; Thu, 12 Jul 2018 23:46:37 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 8570FD69; Thu, 12 Jul 2018 13:40:41 +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 6CC5CD59 for ; Thu, 12 Jul 2018 13:40:40 +0000 (UTC) X-Greylist: whitelisted by SQLgrey-1.7.6 Received: from mail-wr1-f54.google.com (mail-wr1-f54.google.com [209.85.221.54]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 3DD7E76A for ; Thu, 12 Jul 2018 13:40:39 +0000 (UTC) Received: by mail-wr1-f54.google.com with SMTP id q10-v6so21759576wrd.4 for ; Thu, 12 Jul 2018 06:40:39 -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=v7FrJhxrrJVPoIQnCyS5s6JgxtHDSx9OrUhsUqCP8Dk=; b=TIYnzRXTEwGv81DzzWYRi+HaYqMxUE7WxrTchcoLw8H9lqby0X9cAAka5cboSvNkt7 Kjd290Jwz9YukfqlLFif4JF5nlfWQJDmhtG5I78CZmOZK6+n90qdL96e+WuSB6s/Jara lrpPIybzo4h1IiWBNKcRGatdA6vepGnXzFqQ0C7gXJAZZfC8vrOELsuUUd4+TMUitOxG EKrEKZEE6CTfbYrJ8gbo7IRh23oAijCdGfwo1DEHoFaiEJuUtAZkg7m9+BJF858EwiOu ad0XcdkRtC8FrsuGEOTQ7wG+hImS65qFMleW7NXj6bgMmUtexLDT4xDBapcq0ry4yJRl lcLA== X-Gm-Message-State: AOUpUlHMGuBMjbDCdp5IcVXbdMM5TmBWbHGv1RoHWMM5i7lxos8bRVmD Ow1P1UBUycJcx7/hoCxb0koau8F2XfU= X-Google-Smtp-Source: AAOMgpfbsCF9Y7JdyLqEncl4+uP+CX+S2bLkY5bjws0USD7SAbEy4XgMpLXehhGoqbz8KSr5pGiJYg== X-Received: by 2002:adf:92a5:: with SMTP id 34-v6mr1957087wrn.83.1531402837576; Thu, 12 Jul 2018 06:40:37 -0700 (PDT) Received: from redhat.com (red-hat-inc.vlan404.asr1.mad1.gblx.net. [64.215.113.190]) by smtp.gmail.com with ESMTPSA id 5-v6sm8570538wmv.48.2018.07.12.06.40.36 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 12 Jul 2018 06:40:37 -0700 (PDT) From: Jakub Sitnicki To: dev@openvswitch.org Date: Thu, 12 Jul 2018 15:40:13 +0200 Message-Id: <20180712134016.14336-13-jkbs@redhat.com> X-Mailer: git-send-email 2.14.4 In-Reply-To: <20180712134016.14336-1-jkbs@redhat.com> References: <20180712134016.14336-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 v2 12/15] 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 | 213 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 227 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 3dd24d193..ba9b7ca49 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; @@ -214,6 +245,7 @@ parse_options(int argc, char *argv[], struct shash *local_options) OPT_COMMANDS, OPT_OPTIONS, OPT_BOOTSTRAP_CA_CERT, + DAEMON_OPTION_ENUMS, VLOG_OPTION_ENUMS, TABLE_OPTION_ENUMS, SSL_OPTION_ENUMS, @@ -232,6 +264,7 @@ parse_options(int argc, char *argv[], struct shash *local_options) {"leader-only", no_argument, &leader_only, true}, {"no-leader-only", no_argument, &leader_only, false}, {"version", no_argument, NULL, 'V'}, + DAEMON_LONG_OPTIONS, VLOG_LONG_OPTIONS, STREAM_SSL_LONG_OPTIONS, {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT}, @@ -336,6 +369,7 @@ parse_options(int argc, char *argv[], struct shash *local_options) } break; + DAEMON_OPTION_HANDLERS VLOG_OPTION_HANDLERS TABLE_OPTION_HANDLERS(&table_style) STREAM_SSL_OPTION_HANDLERS @@ -529,6 +563,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"); @@ -4562,3 +4597,129 @@ nbctl_cmd_init(void) ctl_init(&nbrec_idl_class, nbrec_table_classes, tables, NULL, nbctl_exit); ctl_register_commands(nbctl_commands); } + +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; + 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); + optind = 0; + 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, + "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 (;;) { + unixctl_server_run(server); + daemonize_complete(); + unixctl_server_wait(server); + if (exiting) { + break; + } + poll_block(); + } + + unixctl_server_destroy(server); +}