[ovs-dev] [PATCH v2 12/15] ovn-nbctl: Initial support for daemon mode.
Jakub Sitnicki
jkbs at redhat.com
Thu Jul 12 13:40:13 UTC 2018
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 <jkbs at redhat.com>
---
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 @@
</dd>
</dl>
+ <h1>Daemon Mode</h1>
+
+ <p>
+ If <code>ovn-nbctl</code> is invoked with the <code>--detach</code>
+ option (see <code>Daemon Options</code>, below), it runs in the
+ background as a daemon and accepts commands from <code>ovs-appctl</code>
+ (or another JSON-RPC client) indefinitely. The currently supported
+ commands are described below.
+ </p>
+
+ <p>
+
+ </p>
+
+ <dl>
+ <dt>
+ <code>run</code> [<var>options</var>] <var>command</var>
+ [<var>arg</var>...] [<code>--</code> [<var>options</var>]
+ <var>command</var> [<var>arg</var>...] ...]
+ </dt>
+ <dd>
+ Instructs the daemon process to run one or more <code>ovn-nbctl</code>
+ commands described above and reply with the results of running these
+ commands. Accepts the <code>--no-wait</code>, <code>--wait</code>,
+ <code>--timeout</code>, <code>--dry-run</code>, <code>--oneline</code>,
+ and the options described under <code>Table Formatting Options</code>
+ in addition to the the command-specific options.
+ </dd>
+
+ <dt><code>exit</code></dt>
+ <dd>Causes <code>ovn-nbctl</code> to gracefully terminate.</dd>
+ </dl>
+
+ <p>
+ Daemon mode is considered experimental.
+ </p>
+
<h1>Options</h1>
<dl>
@@ -982,6 +1019,9 @@
</dd>
</dl>
+ <h2>Daemon Options</h2>
+ <xi:include href="lib/daemon.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
+
<h1>Logging options</h1>
<xi:include href="lib/vlog.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
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 <stdio.h>
#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);
+}
--
2.14.4
More information about the dev
mailing list