[ovs-dev] [PATCH ovn 3/3] ovn-sbctl: Add daemon support.

Mark Michelson mmichels at redhat.com
Mon Mar 29 23:42:53 UTC 2021


Thank you, thank you, thank you for converting the ovn-sb documentation 
to XML.

On 3/25/21 7:26 PM, Ben Pfaff wrote:
> Also rewrite the manpage and convert it to XML for consistency with
> ovn-nbctl, and add tests.
> 
> Signed-off-by: Ben Pfaff <blp at ovn.org>
> ---
>   NEWS                      |   2 +
>   manpages.mk               |  17 -
>   tests/ovn-sbctl.at        |  76 +++--
>   utilities/automake.mk     |   7 +-
>   utilities/ovn-dbctl.c     |  24 +-
>   utilities/ovn-dbctl.h     |   3 +-
>   utilities/ovn-nbctl.c     |   1 +
>   utilities/ovn-sbctl.8.in  | 317 ------------------
>   utilities/ovn-sbctl.8.xml | 583 +++++++++++++++++++++++++++++++++
>   utilities/ovn-sbctl.c     | 670 +++++++-------------------------------
>   10 files changed, 785 insertions(+), 915 deletions(-)
>   delete mode 100644 utilities/ovn-sbctl.8.in
>   create mode 100644 utilities/ovn-sbctl.8.xml
> 
> diff --git a/NEWS b/NEWS
> index 530c5d42fe85..5048433148b8 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -7,6 +7,8 @@ Post-v21.03.0
>       (This may take testing and tuning to be effective.)  This version of OVN
>       requires DDLog 0.36.
>     - Introduce ovn-controller incremetal processing engine statistics
> +  - ovn-sbctl can now run as a daemon (long-lived, background process).
> +    See ovn-sbctl(8) for details.
>   
>   OVN v21.03.0 - 12 Mar 2021
>   -------------------------
> diff --git a/manpages.mk b/manpages.mk
> index 44e544681424..3334b38f943d 100644
> --- a/manpages.mk
> +++ b/manpages.mk
> @@ -10,20 +10,3 @@ lib/common-syn.man:
>   lib/common.man:
>   lib/ovs.tmac:
>   
> -utilities/ovn-sbctl.8: \
> -	utilities/ovn-sbctl.8.in \
> -	lib/common.man \
> -	lib/db-ctl-base.man \
> -	lib/ovs.tmac \
> -	lib/ssl-bootstrap.man \
> -	lib/ssl.man \
> -	lib/table.man \
> -	lib/vlog.man
> -utilities/ovn-sbctl.8.in:
> -lib/common.man:
> -lib/db-ctl-base.man:
> -lib/ovs.tmac:
> -lib/ssl-bootstrap.man:
> -lib/ssl.man:
> -lib/table.man:
> -lib/vlog.man:
> diff --git a/tests/ovn-sbctl.at b/tests/ovn-sbctl.at
> index 2712cc15490c..9334762fd313 100644
> --- a/tests/ovn-sbctl.at
> +++ b/tests/ovn-sbctl.at
> @@ -1,9 +1,14 @@
>   AT_BANNER([ovn-sbctl])
>   
> +OVS_START_SHELL_HELPERS
>   # OVN_SBCTL_TEST_START
>   m4_define([OVN_SBCTL_TEST_START],
> -  [dnl Create databases (ovn-nb, ovn-sb).
> -   AT_KEYWORDS([ovn])
> +  [AT_KEYWORDS([ovn])
> +   AT_CAPTURE_FILE([ovsdb-server.log])
> +   AT_CAPTURE_FILE([ovn-northd.log])
> +   ovn_sbctl_test_start $1])
> +ovn_sbctl_test_start() {
> +   dnl Create databases (ovn-nb, ovn-sb).
>      for daemon in ovn-nb ovn-sb; do
>         AT_CHECK([ovsdb-tool create $daemon.db $abs_top_srcdir/${daemon}.ovsschema])
>      done
> @@ -15,27 +20,54 @@ m4_define([OVN_SBCTL_TEST_START],
>      AT_CHECK([[sed < stderr '
>   /vlog|INFO|opened log file/d
>   /ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']])
> -   AT_CAPTURE_FILE([ovsdb-server.log])
>   
>      dnl Start ovn-northd.
>      AT_CHECK([ovn-northd --detach --no-chdir --pidfile --log-file --ovnnb-db=unix:$OVS_RUNDIR/ovnnb_db.sock --ovnsb-db=unix:$OVS_RUNDIR/ovnsb_db.sock], [0], [], [stderr])
>      on_exit "kill `cat ovn-northd.pid`"
>      AT_CHECK([[sed < stderr '
>   /vlog|INFO|opened log file/d']])
> -   AT_CAPTURE_FILE([ovn-northd.log])
> -])
> +
> +   AS_CASE([$1],
> +     [daemon],
> +       [export OVN_SB_DAEMON=$(ovn-sbctl --pidfile --detach --no-chdir --log-file -vsocket_util:off)
> +        on_exit "kill `cat ovn-sbctl.pid`"],
> +     [direct], [],
> +     [*], [AT_FAIL_IF(:)])
> +}
>   
>   # OVN_SBCTL_TEST_STOP
> -m4_define([OVN_SBCTL_TEST_STOP],
> -  [AT_CHECK([check_logs "$1"])
> -   OVS_APP_EXIT_AND_WAIT([ovn-northd])
> -   OVS_APP_EXIT_AND_WAIT_BY_TARGET([$OVS_RUNDIR/ovnnb_db.ctl], [$OVS_RUNDIR/ovnnb_db.pid])
> -   OVS_APP_EXIT_AND_WAIT_BY_TARGET([$OVS_RUNDIR/ovnsb_db.ctl], [$OVS_RUNDIR/ovnsb_db.pid])])
> +m4_define([OVN_SBCTL_TEST_STOP], [ovn_sbctl_test_stop])
> +ovn_sbctl_test_stop() {
> +  AT_CHECK([check_logs "$1"])
> +  OVS_APP_EXIT_AND_WAIT([ovn-northd])
> +  OVS_APP_EXIT_AND_WAIT_BY_TARGET([$OVS_RUNDIR/ovnnb_db.ctl], [$OVS_RUNDIR/ovnnb_db.pid])
> +  OVS_APP_EXIT_AND_WAIT_BY_TARGET([$OVS_RUNDIR/ovnsb_db.ctl], [$OVS_RUNDIR/ovnsb_db.pid])
> +}
> +OVS_END_SHELL_HELPERS
> +
> +# OVN_SBCTL_TEST(NAME, TITLE, COMMANDS)
> +m4_define([OVN_SBCTL_TEST],
> +   [OVS_START_SHELL_HELPERS
> +    $1() {
> +      $3
> +    }
> +    OVS_END_SHELL_HELPERS
> +
> +    AT_SETUP([ovn-sbctl - $2 - direct])
> +    OVN_SBCTL_TEST_START direct
> +    $1
> +    OVN_SBCTL_TEST_STOP
> +    AT_CLEANUP
> +
> +    AT_SETUP([ovn-sbctl - $2 - daemon])
> +    OVN_SBCTL_TEST_START daemon
> +    $1
> +    OVN_SBCTL_TEST_STOP
> +    AT_CLEANUP])
>   
>   dnl ---------------------------------------------------------------------
>   
> -AT_SETUP([ovn-sbctl - chassis commands])
> -OVN_SBCTL_TEST_START
> +OVN_SBCTL_TEST([ovn_sbctl_chassis_commands], [ovn-sbctl - chassis commands], [
>   ovn_init_db ovn-sb
>   
>   AT_CHECK([ovn-sbctl chassis-add ch0 geneve 1.2.3.4])
> @@ -61,16 +93,14 @@ AT_CHECK([ovn-sbctl -f csv -d bare --no-headings --columns ip,type list encap |
>   1.2.3.5,vxlan
>   ])
>   
> -OVN_SBCTL_TEST_STOP
>   as ovn-sb
>   OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> -AT_CLEANUP
> +as
> +])
>   
>   dnl ---------------------------------------------------------------------
>   
> -AT_SETUP([ovn-sbctl])
> -OVN_SBCTL_TEST_START
> -
> +OVN_SBCTL_TEST([ovn_sbctl_commands], [ovn-sbctl], [
>   AT_CHECK([ovn-nbctl ls-add br-test])
>   AT_CHECK([ovn-nbctl lsp-add br-test vif0])
>   AT_CHECK([ovn-nbctl lsp-set-addresses vif0 f0:ab:cd:ef:01:02])
> @@ -131,20 +161,14 @@ mac                 : [[]]
>   type                : vtep
>   options             : {vtep_logical_switch=l0, vtep_physical_switch=p0}
>   ])
> -
> -OVN_SBCTL_TEST_STOP
> -AT_CLEANUP
> +])
>   
>   dnl ---------------------------------------------------------------------
>   
> -AT_SETUP([ovn-sbctl - connection])
> -OVN_SBCTL_TEST_START
> -
> +OVN_SBCTL_TEST([ovn_sbctl_connection], [ovn-sbctl - connection], [
>   AT_CHECK([ovn-sbctl --inactivity-probe=30000 set-connection ptcp:6641:127.0.0.1 punix:$OVS_RUNDIR/ovnsb_db.sock])
>   AT_CHECK([ovn-sbctl list connection | grep inactivity_probe], [0], [dnl
>   inactivity_probe    : 30000
>   inactivity_probe    : 30000
>   ])
> -
> -OVN_SBCTL_TEST_STOP
> -AT_CLEANUP
> +])
> \ No newline at end of file
> diff --git a/utilities/automake.mk b/utilities/automake.mk
> index 50c0cfded018..a03892f2055a 100644
> --- a/utilities/automake.mk
> +++ b/utilities/automake.mk
> @@ -14,7 +14,6 @@ man_MANS += \
>       utilities/ovn-appctl.8
>   
>   MAN_ROOTS += \
> -    utilities/ovn-sbctl.8.in \
>       utilities/ovn-detrace.1.in
>   
>   # Docker drivers
> @@ -30,6 +29,7 @@ EXTRA_DIST += \
>       utilities/ovn-docker-overlay-driver.in \
>       utilities/ovn-docker-underlay-driver.in \
>       utilities/ovn-nbctl.8.xml \
> +    utilities/ovn-sbctl.8.xml \
>       utilities/ovn-ic-nbctl.8.xml \
>       utilities/ovn-ic-sbctl.8.xml \
>       utilities/ovn-appctl.8.xml \
> @@ -79,7 +79,10 @@ utilities_ovn_nbctl_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la $(OVS_LIBD
>   
>   # ovn-sbctl
>   bin_PROGRAMS += utilities/ovn-sbctl
> -utilities_ovn_sbctl_SOURCES = utilities/ovn-sbctl.c
> +utilities_ovn_sbctl_SOURCES = \
> +    utilities/ovn-dbctl.c \
> +    utilities/ovn-dbctl.h \
> +    utilities/ovn-sbctl.c
>   utilities_ovn_sbctl_LDADD = lib/libovn.la $(OVSDB_LIBDIR)/libovsdb.la $(OVS_LIBDIR)/libopenvswitch.la
>   
>   # ovn-ic-nbctl
> diff --git a/utilities/ovn-dbctl.c b/utilities/ovn-dbctl.c
> index 730e84f5e770..1b164f83e8ab 100644
> --- a/utilities/ovn-dbctl.c
> +++ b/utilities/ovn-dbctl.c
> @@ -328,7 +328,8 @@ enum {
>   };
>   
>   static char * OVS_WARN_UNUSED_RESULT
> -handle_main_loop_option(int opt, const char *arg, bool *handled)
> +handle_main_loop_option(const struct ovn_dbctl_options *dbctl_options,
> +                        int opt, const char *arg, bool *handled)
>   {
>       ovs_assert(handled);
>       *handled = true;
> @@ -339,11 +340,16 @@ handle_main_loop_option(int opt, const char *arg, bool *handled)
>           break;
>   
>       case OPT_NO_WAIT:
> +        if (!dbctl_options->allow_wait) {
> +            return xstrdup("--no-wait not supported");
> +        }
>           wait_type = NBCTL_WAIT_NONE;
>           break;
>   
>       case OPT_WAIT:
> -        if (!strcmp(arg, "none")) {
> +        if (!dbctl_options->allow_wait) {
> +            return xstrdup("--wait not supported");
> +        } else if (!strcmp(arg, "none")) {
>               wait_type = NBCTL_WAIT_NONE;
>           } else if (!strcmp(arg, "sb")) {
>               wait_type = NBCTL_WAIT_SB;
> @@ -356,6 +362,9 @@ handle_main_loop_option(int opt, const char *arg, bool *handled)
>           break;
>   
>       case OPT_PRINT_WAIT_TIME:
> +        if (!dbctl_options->allow_wait) {
> +            return xstrdup("--print-wait-time not supported");
> +        }
>           print_wait_time = true;
>           break;
>   
> @@ -487,7 +496,8 @@ apply_options_direct(const struct ovn_dbctl_options *dbctl_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);
> +        char *error = handle_main_loop_option(dbctl_options,
> +                                              po->o->val, po->arg, &handled);
>           if (error) {
>               ctl_fatal("%s", error);
>           }
> @@ -835,7 +845,8 @@ find_option_by_value(const struct option *options, int value)
>   }
>   
>   static char * OVS_WARN_UNUSED_RESULT
> -server_parse_options(int argc, char *argv[], struct shash *local_options,
> +server_parse_options(const struct ovn_dbctl_options *dbctl_options,
> +                     int argc, char *argv[], struct shash *local_options,
>                        int *n_options_p)
>   {
>       static const struct option global_long_options[] = {
> @@ -866,7 +877,7 @@ server_parse_options(int argc, char *argv[], struct shash *local_options,
>           }
>   
>           bool handled;
> -        error = handle_main_loop_option(c, optarg, &handled);
> +        error = handle_main_loop_option(dbctl_options, c, optarg, &handled);
>           if (error) {
>               goto out;
>           }
> @@ -968,7 +979,8 @@ server_cmd_run(struct unixctl_conn *conn, int argc, const char **argv_,
>       /* Parse commands & options. */
>       char *args = process_escape_args(argv);
>       shash_init(&local_options);
> -    error = server_parse_options(argc, argv, &local_options, &n_options);
> +    error = server_parse_options(dbctl_options,
> +                                 argc, argv, &local_options, &n_options);
>       if (error) {
>           unixctl_command_reply_error(conn, error);
>           goto out;
> diff --git a/utilities/ovn-dbctl.h b/utilities/ovn-dbctl.h
> index 5accf3c5e028..a1fbede6b5ce 100644
> --- a/utilities/ovn-dbctl.h
> +++ b/utilities/ovn-dbctl.h
> @@ -15,7 +15,7 @@
>   #ifndef OVN_DBCTL_H
>   #define OVN_DBCTL_H 1
>   
> -/* ovn-nbctl infrastructure code. */
> +/* Common code for ovn-sbctl and ovn-nbctl. */
>   
>   #include <stdbool.h>
>   #include "ovsdb-idl.h"
> @@ -31,6 +31,7 @@ enum nbctl_wait_type {
>   struct ovn_dbctl_options {
>       const char *db_version;     /* Database schema version. */
>       const char *default_db;     /* Default database remote. */
> +    bool allow_wait;            /* Allow --wait and related options? */
>   
>       /* Names of important environment variables. */
>       const char *options_env_var_name; /* OVN_??_OPTIONS. */
> diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c
> index 8e8a5f8cfde9..dbaf928c168c 100644
> --- a/utilities/ovn-nbctl.c
> +++ b/utilities/ovn-nbctl.c
> @@ -5982,6 +5982,7 @@ main(int argc, char *argv[])
>       struct ovn_dbctl_options dbctl_options = {
>           .db_version = nbrec_get_db_version(),
>           .default_db = default_nb_db(),
> +        .allow_wait = true,
>   
>           .options_env_var_name = "OVN_NBCTL_OPTIONS",
>           .daemon_env_var_name = "OVN_NBCTL_DAEMON",
> diff --git a/utilities/ovn-sbctl.8.in b/utilities/ovn-sbctl.8.in
> deleted file mode 100644
> index 153e72e6c28d..000000000000
> --- a/utilities/ovn-sbctl.8.in
> +++ /dev/null
> @@ -1,317 +0,0 @@
> -.\" -*- nroff -*-
> -.so lib/ovs.tmac
> -.TH ovn\-sbctl 8 "@VERSION@" "OVN" "OVN Manual"
> -.\" This program's name:
> -.ds PN ovn\-sbctl
> -.
> -.SH NAME
> -ovn\-sbctl \- utility for querying and configuring \fBOVN_Southbound\fR database
> -.
> -.SH SYNOPSIS
> -\fBovn\-sbctl\fR [\fIoptions\fR] \fB\-\-\fR [\fIoptions\fR] \fIcommand
> -\fR[\fIargs\fR] [\fB\-\-\fR [\fIoptions\fR] \fIcommand \fR[\fIargs\fR]]...
> -.
> -.SH DESCRIPTION
> -The \fBovn\-sbctl\fR program configures the \fBOVN_Southbound\fR database
> -by providing a high\-level interface to its configuration database.  See
> -\fBovn\-sb\fR(5) for comprehensive documentation of the database schema.
> -.PP
> -\fBovn\-sbctl\fR connects to an \fBovsdb\-server\fR process that
> -maintains an OVN_Southbound configuration database.  Using this
> -connection, it queries and possibly applies changes to the database,
> -depending on the supplied commands.
> -.PP
> -\fBovn\-sbctl\fR can perform any number of commands in a single run,
> -implemented as a single atomic transaction against the database.
> -.PP
> -The \fBovn\-sbctl\fR command line begins with global options (see
> -\fBOPTIONS\fR below for details).  The global options are followed by
> -one or more commands.  Each command should begin with \fB\-\-\fR by
> -itself as a command-line argument, to separate it from the following
> -commands.  (The \fB\-\-\fR before the first command is optional.)  The
> -command
> -itself starts with command-specific options, if any, followed by the
> -command name and any arguments.
> -.
> -.SH OPTIONS
> -.
> -The following options affect the behavior of \fBovn\-sbctl\fR as a
> -whole.  Some individual commands also accept their own options, which
> -are given just before the command name.  If the first command on the
> -command line has options, then those options must be separated from
> -the global options by \fB\-\-\fR.
> -.
> -.IP "\fB\-\-db=\fIserver\fR"
> -The OVSDB database remote to contact.  If the \fBOVN_SB_DB\fR
> -environment variable is set, its value is used as the default.
> -Otherwise, the default is \fBunix:@RUNDIR@/ovnsb_db.sock\fR, but this
> -default is unlikely to be useful outside of single-machine OVN test
> -environments.
> -.IP
> -\fIserver\fR may be an OVSDB active or passive connection method,
> -e.g. \fBssl:192.168.10.5:6640\fR, as described in \fBovsdb\fR(7).
> -.
> -.IP "\fB\-\-leader\-only\fR"
> -.IQ "\fB\-\-no\-leader\-only\fR"
> -By default, or with \fB\-\-leader\-only\fR, when the database server
> -is a clustered database, \fBovn\-sbctl\fR will avoid servers other
> -than the cluster leader.  This ensures that any data that
> -\fBovn\-sbctl\fR reads and reports is up-to-date.  With
> -\fB\-\-no\-leader\-only\fR, \fBovn\-sbctl\fR will use any server in
> -the cluster, which means that for read-only transactions it can report
> -and act on stale data (transactions that modify the database are
> -always serialized even with \fB\-\-no\-leader\-only\fR).  Refer to
> -\fBUnderstanding Cluster Consistency\fR in \fBovsdb\fR(7) for more
> -information.
> -.
> -.IP "\fB\-\-no\-syslog\fR"
> -By default, \fBovn\-sbctl\fR logs its arguments and the details of any
> -changes that it makes to the system log.  This option disables this
> -logging.
> -.IP
> -This option is equivalent to \fB\-\-verbose=sbctl:syslog:warn\fR.
> -.
> -.IP "\fB\-\-oneline\fR"
> -Modifies the output format so that the output for each command is printed
> -on a single line.  New-line characters that would otherwise separate
> -lines are printed as \fB\\n\fR, and any instances of \fB\\\fR that
> -would otherwise appear in the output are doubled.
> -Prints a blank line for each command that has no output.
> -This option does not affect the formatting of output from the
> -\fBlist\fR or \fBfind\fR commands; see \fBTable Formatting Options\fR
> -below.
> -.
> -.IP "\fB\-\-dry\-run\fR"
> -Prevents \fBovn\-sbctl\fR from actually modifying the database.
> -.
> -.IP "\fB\-t \fIsecs\fR"
> -.IQ "\fB\-\-timeout=\fIsecs\fR"
> -By default, or with a \fIsecs\fR of \fB0\fR, \fBovn\-sbctl\fR waits
> -forever for a response from the database.  This option limits runtime
> -to approximately \fIsecs\fR seconds.  If the timeout expires,
> -\fBovn\-sbctl\fR will exit with a \fBSIGALRM\fR signal.  (A timeout
> -would normally happen only if the database cannot be contacted, or if
> -the system is overloaded.)
> -.
> -.IP "\fBOVN_SBCTL_OPTIONS\fR"
> -User can set one or more options using \fBOVN_SBCTL_OPTIONS\fR environment
> -variable. Under the Bourne shell this might be done like this:
> -export \fBOVN_SBCTL_OPTIONS\fR"="--db=unix:sb1.ovsdb --no-leader-only".
> -However user can still over-ride environment options by passing different
> -options in cli. When the environment variable is no longer needed, unset it,
> -e.g.: unset \fBOVN_SBCTL_OPTIONS\fR"
> -.
> -.so lib/vlog.man
> -.so lib/common.man
> -.
> -.SS "Table Formatting Options"
> -These options control the format of output from the \fBlist\fR and
> -\fBfind\fR commands.
> -.so lib/table.man
> -.
> -.SS "Public Key Infrastructure Options"
> -.so lib/ssl-bootstrap.man
> -.so lib/ssl.man
> -.
> -.SH COMMANDS
> -The commands implemented by \fBovn\-sbctl\fR are described in the
> -sections below.
> -.SS "OVN_Southbound Commands"
> -These commands work with an \fBOVN_Southbound\fR database as a whole.
> -.
> -.IP "\fBinit\fR"
> -Initializes the database, if it is empty.  If the database has already
> -been initialized, this command has no effect.
> -.
> -.IP "\fBshow\fR"
> -Prints a brief overview of the database contents.
> -.
> -.SS "Chassis Commands"
> -These commands manipulate \fBOVN_Southbound\fR chassis.
> -.
> -.IP "[\fB\-\-may\-exist\fR] \fBchassis\-add \fIchassis\fR \fIencap-type\fR \fIencap-ip\fR"
> -Creates a new chassis named \fIchassis\fR.  \fIencap-type\fR is a
> -comma-separated list of tunnel types.  The chassis will have
> -one encap entry for each specified tunnel type with \fIencap-ip\fR
> -as the destination IP for each.
> -.IP
> -Without \fB\-\-may\-exist\fR, attempting to create a chassis that
> -exists is an error.  With \fB\-\-may\-exist\fR, this command does
> -nothing if \fIchassis\fR already exists.
> -.
> -.IP "[\fB\-\-if\-exists\fR] \fBchassis\-del \fIchassis\fR"
> -Deletes \fIchassis\fR and its \fIencaps\fR and \fIgateway_ports\fR.
> -.IP
> -Without \fB\-\-if\-exists\fR, attempting to delete a chassis that does
> -not exist is an error.  With \fB\-\-if\-exists\fR, attempting to
> -delete a chassis that does not exist has no effect.
> -.
> -.SS "Port binding Commands"
> -.
> -These commands manipulate \fBOVN_Southbound\fR port bindings.
> -.
> -.IP "[\fB\-\-may\-exist\fR] \fBlsp\-bind \fIlogical-port\fR \fIchassis\fR"
> -Binds the logical port named \fIlogical-port\fR to \fIchassis\fR.
> -.IP
> -Without \fB\-\-may\-exist\fR, attempting to bind a logical port that
> -has already been bound is an error.  With \fB\-\-may\-exist\fR, this
> -command does nothing if \fIlogical-port\fR has already been bound to
> -a chassis.
> -.
> -.IP "[\fB\-\-if\-exists\fR] \fBlsp\-unbind\fR \fIlogical-port\fR"
> -Resets the binding of \fIlogical-port\fR to \fINULL\fR.
> -.IP
> -Without \fB\-\-if\-exists\fR, attempting to unbind a logical port
> -that is not bound is an error.  With \fB\-\-if\-exists\fR, attempting
> -to unbind logical port that is not bound has no effect.
> -.
> -.SS "Logical Flow Commands"
> -.
> -.IP "[\fB\-\-uuid\fR] [\fB\-\-ovs\fR[\fB=\fIremote\fR]] [\fB\-\-stats\fR] [\fB\-\-vflows\fR] \fBlflow\-list\fR [\fIlogical-datapath\fR] [\fIlflow\fR...]"
> -List logical flows.  If \fIlogical-datapath\fR is specified, only list
> -flows for that logical datapath.  The \fIlogical-datapath\fR may be
> -given as a UUID or as a datapath name (reporting an error if multiple
> -datapaths have the same name).
> -.IP
> -If at least one \fIlflow\fR is given, only matching logical flows, if
> -any, are listed.  Each \fIlflow\fR may be specified as a UUID or the
> -first few characters of a UUID, optionally prefixed by \fB0x\fR.
> -(Because \fBovn\-controller\fR sets OpenFlow flow cookies to the first
> -32 bits of the corresponding logical flow's UUID, this makes it easy
> -to look up the logical flow that generated a particular OpenFlow
> -flow.)
> -.IP
> -If \fB\-\-uuid\fR is specified, the output includes the first 32 bits
> -of each logical flow's UUID.  This makes it easier to find the
> -OpenFlow flows that correspond to a given logical flow.
> -.IP
> -If \fB\-\-ovs\fR is included, \fBovn\-sbctl\fR attempts to obtain and
> -display the OpenFlow flows that correspond to each OVN logical flow.
> -To do so, \fBovn\-sbctl\fR connects to \fIremote\fR (by default,
> -\fBunix:@RUNDIR@/br-int.mgmt\fR) over OpenFlow and retrieves the
> -flows.  If \fIremote\fR is specified, it must be an active OpenFlow
> -connection method described in \fBovsdb\fR(7).  Please see the
> -discussion of the similar \fB\-\-ovs\fR option in \fBovn-trace\fR(8)
> -for more information about the OpenFlow flow output.
> -.IP
> -By default, OpenFlow flow output includes only match and actions.  Add
> -\fB\-\-stats\fR to include all OpenFlow information, such as packet
> -and byte counters, duration, and timeouts.
> -.IP
> -If \fB\-\-vflows\fR is included, other southbound database records directly
> -used for generating OpenFlow flows are also listed. This includes:
> -\fIport-bindings\fR, \fImac-bindings\fR, \fImulticast-groups\fR,
> -\fIchassis\fR.  The \fB\-\-ovs\fR and \fB\-\-stats\fR can also be used in
> -conjunction with \fB\-\-vflows\fR.
> -.
> -.IP "[\fB\-\-uuid\fR] \fBdump\-flows\fR [\fIlogical-datapath\fR]"
> -Alias for \fBlflow\-list\fB.
> -.
> -.SS "Remote Connectivity Commands"
> -.
> -These commands manipulate the \fBconnections\fR column in the \fBSB_Global\fR
> -table and rows in the \fBConnection\fR table.  When \fBovsdb\-server\fR
> -is configured to use the \fBconnections\fR column for OVSDB connections,
> -this allows the administrator to use \fBovn\-sbctl\fR to configure database
> -connections.
> -.
> -.IP "\fBget\-connection\fR"
> -Prints the configured connection(s).
> -.
> -.IP "\fBdel\-connection\fR"
> -Deletes the configured connection(s).
> -.
> -.IP "\fBset\-connection\fR [\fIaccess\-specifier\fR] \fItarget\fR\&..."
> -Sets the configured manager target or targets.  Each \fItarget\fR may
> -may be an OVSDB active or passive connection method,
> -e.g. \fBpssl:6640\fR, as described in \fBovsdb\fR(7),
> -optionally preceded by an optional access-specifier (\fBread\-only\fR or
> -\fBread\-write\fR).
> -If provided, the effect of the access specifier persists for subsequent
> -targets until changed by another access specifier.
> -.
> -.SS "SSL Configuration"
> -When \fBovsdb\-server\fR is configured to connect using SSL, the
> -following parameters are required:
> -.TP
> -\fIprivate-key\fR
> -Specifies a PEM file containing the private key used for SSL connections.
> -.TP
> -\fIcertificate\fR
> -Specifies a PEM file containing a certificate, signed by the
> -certificate authority (CA) used by the connection peers, that
> -certifies the private key, identifying a trustworthy peer.
> -.TP
> -\fIca-cert\fR
> -Specifies a PEM file containing the CA certificate used to verify that
> -the connection peers are trustworthy.
> -.PP
> -These SSL settings apply to all SSL connections made by the southbound
> -database server.
> -.
> -.IP "\fBget\-ssl\fR"
> -Prints the SSL configuration.
> -.
> -.IP "\fBdel\-ssl\fR"
> -Deletes the current SSL configuration.
> -.
> -.IP "[\fB\-\-bootstrap\fR] \fBset\-ssl\fR \fIprivate-key\fR \fIcertificate\fR \fIca-cert\fR [\fIssl-protocol-list\fR [\fIssl-cipher-list\fR]]"
> -Sets the SSL configuration.  The \fB\-\-bootstrap\fR option is described
> -below.
> -.
> -.ST "CA Certificate Bootstrap"
> -.PP
> -Ordinarily, all of the files named in the SSL configuration must exist
> -before SSL connectivity can be used.  However, if the \fIca-cert\fR file
> -does not exist and the \fB\-\-bootstrap\fR
> -option is given, then \fBovsdb\-server\fR will attempt to obtain the
> -CA certificate from the target on its first SSL connection and
> -save it to the named PEM file.  If it is successful, it will
> -immediately drop the connection and reconnect, and from then on all
> -SSL connections must be authenticated by a certificate signed by the
> -CA certificate thus obtained.
> -.PP
> -\fBThis option exposes the SSL connection to a man-in-the-middle
> -attack obtaining the initial CA certificate\fR, but it may be useful
> -for bootstrapping.
> -.PP
> -This option is only useful if the SSL peer sends its CA certificate
> -as part of the SSL certificate chain.  The SSL protocol does not
> -require the controller to send the CA certificate.
> -.
> -.SS "Database Commands"
> -.
> -These commands query and modify the contents of \fBovsdb\fR tables.
> -They are a slight abstraction of the \fBovsdb\fR interface and as such
> -they operate at a lower level than other \fBovs\-sbctl\fR commands.
> -.PP
> -.ST "Identifying Tables, Records, and Columns"
> -.PP
> -Each of these commands has a \fItable\fR parameter to identify a table
> -within the database.  Many of them also take a \fIrecord\fR parameter
> -that identifies a particular record within a table.  The \fIrecord\fR
> -parameter may be the UUID for a record, and many tables offer
> -additional ways to identify records.  Some commands also take
> -\fIcolumn\fR parameters that identify a particular field within the
> -records in a table.
> -.PP
> -For a list of tables and their columns, see \fBovn\-sb\fR(5) or
> -see the table listing from the \fB--help\fR option.
> -.PP
> -Record names must be specified in full and with correct
> -capitalization, except that UUIDs may be abbreviated to their first 4
> -(or more) hex digits, as long as that is unique within the table.
> -Names of tables and columns are not case-sensitive, and \fB\-\fR and
> -\fB_\fR are treated interchangeably.  Unique abbreviations of table
> -and column names are acceptable, e.g. \fBaddr\fR or \fBa\fR is
> -sufficient to identify the \fBAddress_Set\fR table.
> -.
> -.so lib/db-ctl-base.man
> -.SH "EXIT STATUS"
> -.IP "0"
> -Successful program execution.
> -.IP "1"
> -Usage, syntax, or configuration file error.
> -.SH "SEE ALSO"
> -.
> -.BR ovn\-sb (5).
> diff --git a/utilities/ovn-sbctl.8.xml b/utilities/ovn-sbctl.8.xml
> new file mode 100644
> index 000000000000..948933392e01
> --- /dev/null
> +++ b/utilities/ovn-sbctl.8.xml
> @@ -0,0 +1,583 @@
> +<?xml version="1.0" encoding="utf-8"?>
> +<manpage program="ovn-sbctl" section="8" title="ovn-sbctl">
> +    <h1>Name</h1>
> +    <p>ovn-sbctl -- Open Virtual Network southbound db management utility</p>
> +
> +    <h1>Synopsis</h1>
> +    <p><code>ovn-sbctl</code> [<var>options</var>] <var>command</var> [<var>arg</var>...]</p>
> +
> +    <h1>Description</h1>
> +
> +    <p>
> +      The <code>ovn-sbctl</code> program configures the
> +      <code>OVN_Southbound</code> database by providing a high-level interface
> +      to its configuration database.  See <code>ovn-sb</code>(5) for
> +      comprehensive documentation of the database schema.
> +    </p>
> +
> +    <p>
> +      <code>ovn-sbctl</code> connects to an <code>ovsdb-server</code> process
> +      that maintains an OVN_Southbound configuration database.  Using this
> +      connection, it queries and possibly applies changes to the database,
> +      depending on the supplied commands.
> +    </p>
> +
> +    <p>
> +      <code>ovn-sbctl</code> can perform any number of commands in a single
> +      run, implemented as a single atomic transaction against the database.
> +    </p>
> +
> +    <p>
> +      The <code>ovn-sbctl</code> command line begins with global options (see
> +      <code>OPTIONS</code> below for details).  The global options are followed
> +      by one or more commands.  Each command should begin with <code>--</code>
> +      by itself as a command-line argument, to separate it from the following
> +      commands.  (The <code>--</code> before the first command is optional.)
> +      The command itself starts with command-specific options, if any, followed
> +      by the command name and any arguments.
> +    </p>
> +
> +    <h1>Daemon Mode</h1>
> +
> +    <p>
> +      When it is invoked in the most ordinary way, <code>ovn-sbctl</code>
> +      connects to an OVSDB server that hosts the southbound database, retrieves
> +      a partial copy of the database that is complete enough to do its work,
> +      sends a transaction request to the server, and receives and processes the
> +      server's reply.  In common interactive use, this is fine, but if the
> +      database is large, the step in which <code>ovn-sbctl</code> retrieves a
> +      partial copy of the database can take a long time, which yields poor
> +      performance overall.
> +    </p>
> +
> +    <p>
> +      To improve performance in such a case, <code>ovn-sbctl</code> offers a
> +      "daemon mode," in which the user first starts <code>ovn-sbctl</code>
> +      running in the background and afterward uses the daemon to execute
> +      operations.  Over several <code>ovn-sbctl</code> command invocations,
> +      this performs better overall because it retrieves a copy of the database
> +      only once at the beginning, not once per program run.
> +    </p>
> +
> +    <p>
> +      Use the <code>--detach</code> option to start an <code>ovn-sbctl</code>
> +      daemon.  With this option, <code>ovn-sbctl</code> prints the name of a
> +      control socket to stdout.  The client should save this name in
> +      environment variable <env>OVN_SB_DAEMON</env>.  Under the Bourne shell
> +      this might be done like this:
> +    </p>
> +
> +    <pre fixed="yes">
> +      export OVN_SB_DAEMON=$(ovn-sbctl --pidfile --detach)
> +    </pre>
> +
> +    <p>
> +      When <env>OVN_SB_DAEMON</env> is set, <code>ovn-sbctl</code>
> +      automatically and transparently uses the daemon to execute its commands.
> +    </p>
> +
> +    <p>
> +      When the daemon is no longer needed, kill it and unset the environment
> +      variable, e.g.:
> +    </p>
> +
> +    <pre fixed="yes">
> +      kill $(cat $OVN_RUNDIR/ovn-sbctl.pid)
> +      unset OVN_SB_DAEMON
> +    </pre>
> +
> +    <p>
> +      When using daemon mode, an alternative to the <env>OVN_SB_DAEMON</env>
> +      environment variable is to specify a path for the Unix socket. When
> +      starting the ovn-sbctl daemon, specify the <code>-u</code> option with a
> +      full path to the location of the socket file. Here is an exmple:
> +    </p>
> +
> +    <pre fixed="yes">
> +      ovn-sbctl --detach -u /tmp/mysock.ctl
> +    </pre>
> +
> +    <p>
> +      Then to connect to the running daemon, use the <code>-u</code> option
> +      with the full path to the socket created when the daemon was started:
> +    </p>
> +
> +    <pre fixed="yes">
> +      ovn-sbctl -u /tmp/mysock.ctl show
> +    </pre>
> +
> +    <p>
> +      Daemon mode is experimental.
> +    </p>
> +
> +    <h3>Daemon Commands</h3>
> +
> +    <p>
> +      Daemon mode is internally implemented using the same mechanism used by
> +      <code>ovs-appctl</code>.  One may also use <code>ovs-appctl</code>

Like with patch 1, I think this should be ovn-appctl.

> +      directly with the following commands:
> +    </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-sbctl</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-sbctl</code> to gracefully terminate.</dd>
> +    </dl>
> +
> +    <h1>Options</h1>
> +
> +    <p>
> +      The options listed below affect the behavior of <code>ovn-sbctl</code> as
> +      a whole.  Some individual commands also accept their own options, which
> +      are given just before the command name.  If the first command on the
> +      command line has options, then those options must be separated from the
> +      global options by <code>--</code>.
> +    </p>
> +
> +    <p>
> +      <code>ovn-sbctl</code> also accepts options from the
> +      <env>OVN_SBCTL_OPTIONS</env> environment variable, in the same format as
> +      on the command line.  Options from the command line override those in the
> +      environment.
> +    </p>
> +
> +    <dl>
> +      <dt><code>--db</code> <var>database</var></dt>
> +      <dd>
> +        The OVSDB database remote to contact.  If the <env>OVN_SB_DB</env>
> +        environment variable is set, its value is used as the default.
> +        Otherwise, the default is <code>unix:@RUNDIR@/ovnsb_db.sock</code>, but
> +        this default is unlikely to be useful outside of single-machine OVN
> +        test environments.
> +      </dd>
> +
> +      <dt><code>--leader-only</code></dt>
> +      <dt><code>--no-leader-only</code></dt>
> +      <dd>
> +        By default, or with <code>--leader-only</code>, when the database
> +        server is a clustered database, <code>ovn-sbctl</code> will avoid
> +        servers other than the cluster leader.  This ensures that any data that
> +        <code>ovn-sbctl</code> reads and reports is up-to-date.  With
> +        <code>--no-leader-only</code>, <code>ovn-sbctl</code> will use any
> +        server in the cluster, which means that for read-only transactions it
> +        can report and act on stale data (transactions that modify the database
> +        are always serialized even with <code>--no-leader-only</code>).  Refer
> +        to <code>Understanding Cluster Consistency</code> in
> +        <code>ovsdb</code>(7) for more information.
> +      </dd>
> +
> +      <dt><code>--shuffle-remotes</code></dt>
> +      <dt><code>--no-shuffle-remotes</code></dt>
> +      <dd>
> +        By default, or with <code>--shuffle-remotes</code>, when there are
> +        multiple remotes specified in the OVSDB connection string specified by
> +        <code>--db</code> or the <env>OVN_SB_DB</env> environment variable, the
> +        order of the remotes will be shuffled before the client tries to
> +        connect.  The remotes will be shuffled only once to a new order before
> +        the first connection attempt.  The following retries, if any, will
> +        follow the same new order.  The default behavior is to make sure
> +        clients of a clustered database can distribute evenly to all memembers
> +        of the cluster.  With <code>--no-shuffle-remotes</code>,
> +        <code>ovn-sbctl</code> will use the original order specified in the
> +        connection string to connect.  This allows user to specify the
> +        preferred order, which is particularly useful for testing.
> +      </dd>
> +
> +      <dt><code>--no-syslog</code></dt>
> +      <dd>
> +        <p>
> +          By default, <code>ovn-sbctl</code> logs its arguments and the details
> +          of any changes that it makes to the system log.  This option disables
> +          this logging.
> +        </p>
> +
> +        <p>
> +          This option is equivalent to
> +          <code>--verbose=sbctl:syslog:warn</code>.
> +        </p>
> +      </dd>
> +
> +      <dt><code>--oneline</code></dt>
> +      <dd>
> +        Modifies the output format so that the output for each command is
> +        printed on a single line.  New-line characters that would otherwise
> +        separate lines are printed as \fB\\n\fR, and any instances of \fB\\\fR
> +        that would otherwise appear in the output are doubled.  Prints a blank
> +        line for each command that has no output.  This option does not affect
> +        the formatting of output from the <code>list</code> or
> +        <code>find</code> commands; see <code>Table Formatting Options</code>
> +        below.
> +      </dd>
> +
> +      <dt><code>--dry-run</code></dt>
> +      <dd>
> +        Prevents <code>ovn-sbctl</code> from actually modifying the database.
> +      </dd>
> +
> +      <dt><code>-t <var>secs</var></code></dt>
> +      <dt><code>--timeout=<var>secs</var></code></dt>
> +      <dd>
> +        By default, or with a <var>secs</var> of <code>0</code>,
> +        <code>ovn-sbctl</code> waits forever for a response from the database.
> +        This option limits runtime to approximately <var>secs</var> seconds.
> +        If the timeout expires, <code>ovn-sbctl</code> will exit with a
> +        <code>SIGALRM</code> signal.  (A timeout would normally happen only if
> +        the database cannot be contacted, or if the system is overloaded.)
> +      </dd>
> +    </dl>
> +
> +    <h2>Daemon Options</h2>
> +    <xi:include href="lib/daemon.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
> +
> +    <h2>Logging options</h2>
> +    <xi:include href="lib/vlog.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
> +
> +    <h2>Table Formatting Options</h2>
> +    These options control the format of output from the <code>list</code> and
> +    <code>find</code> commands.
> +    <xi:include href="lib/table.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
> +
> +    <h2>PKI Options</h2>
> +    <p>
> +      PKI configuration is required to use SSL for the connection to the
> +      database.
> +    </p>
> +    <xi:include href="lib/ssl.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
> +    <xi:include href="lib/ssl-bootstrap.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
> +
> +    <h2>Other Options</h2>
> +
> +    <xi:include href="lib/common.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
> +
> +    <h1>Commands</h1>
> +
> +    <p>
> +      The following sections describe the commands that <code>ovn-sbctl</code>
> +      supports.
> +    </p>
> +
> +    <h2>OVN_Southbound Commands</h2>
> +
> +    <p>
> +      These commands work with an <code>OVN_Southbound</code> database as a
> +      whole.
> +    </p>
> +
> +    <dl>
> +      <dt><code>init</code></dt>
> +      <dd>
> +        Initializes the database, if it is empty.  If the database has already
> +        been initialized, this command has no effect.
> +      </dd>
> +
> +      <dt><code>show</code></dt>
> +      <dd>
> +        Prints a brief overview of the database contents.
> +      </dd>
> +    </dl>
> +
> +    <h2>Chassis Commands</h2>
> +
> +    <p>
> +      These commands manipulate <code>OVN_Southbound</code> chassis.
> +    </p>
> +
> +    <dl>
> +      <dt>[<code>--may-exist</code>] <code>chassis-add <var>chassis</var> <var>encap-type</var> <var>encap-ip</var></code></dt>
> +
> +      <dd>
> +        <p>
> +          Creates a new chassis named <var>chassis</var>.
> +          <var>encap-type</var> is a comma-separated list of tunnel types.  The
> +          chassis will have one encap entry for each specified tunnel type with
> +          <var>encap-ip</var> as the destination IP for each.
> +        </p>
> +
> +        <p>
> +          Without \fB\-\-may\-exist\fR, attempting to create a chassis that
> +          exists is an error.  With \fB\-\-may\-exist\fR, this command does
> +          nothing if <var>chassis</var> already exists.
> +        </p>
> +      </dd>
> +
> +      <dt>[<code>--if-exists</code>] <var>chassis-del <var>chassis</var></var></dt>
> +      <dd>
> +        <p>
> +          Deletes <var>chassis</var> and its <var>encaps</var> and
> +          <var>gateway_ports</var>.
> +        </p>
> +
> +        <p>
> +          Without <code>--if-exists</code>, attempting to delete a chassis that
> +          does not exist is an error.  With <code>--if-exists</code> attempting
> +          to delete a chassis that does not exist has no effect.
> +        </p>
> +      </dd>
> +    </dl>
> +
> +    <h2>Port Binding Commands</h2>
> +
> +    <p>
> +      These commands manipulate <code>OVN_Southbound</code> port bindings.
> +    </p>
> +
> +    <dl>
> +      <dt>[<code>--may-exist</code>] <code>lsp-bind <var>logical-port</var> <var>chassis</var></code></dt>
> +      <dd>
> +        <p>
> +          Binds the logical port named <var>logical-port</var> to
> +          <var>chassis</var>.
> +        </p>
> +
> +        <p>
> +          Without <code>--may-exist</code>, attempting to bind a logical port
> +          that has already been bound is an error.  With
> +          <code>--may-exist</code>, this command does nothing if
> +          <var>logical-port</var> has already been bound to a chassis.
> +        </p>
> +      </dd>
> +
> +      <dt>[<code>--if-exists</code>] <code>lsp-unbind <var>logical-port</var></code></dt>
> +      <dd>
> +        <p>
> +          Removes the binding of <var>logical-port</var>.
> +        </p>
> +
> +        <p>
> +          Without <code>--if-exists</code>, attempting to unbind a logical port
> +          that is not bound is an error.  With <code>--if-exists</code>,
> +          attempting to unbind logical port that is not bound has no effect.
> +        </p>
> +      </dd>
> +    </dl>
> +
> +    <h2>Logical Flow Commands</h2>
> +
> +    <dl>
> +      <dt>[<code>--uuid</code>] [<code>--ovs</code>[<code>=<var>remote</var>]</code>] [<code>--stats</code>] [<code>--vflows</code>] <code>lflow-list</code> [<var>logical-datapath</var>] [<var>lflow</var>...]</dt>
> +
> +      <dd>
> +        <p>
> +          List logical flows.  If <var>logical-datapath</var> is specified,
> +          only list flows for that logical datapath.  The
> +          <var>logical-datapath</var> may be given as a UUID or as a datapath
> +          name (reporting an error if multiple datapaths have the same name).
> +        </p>
> +
> +        <p>
> +          If at least one <var>lflow</var> is given, only matching logical
> +          flows, if any, are listed.  Each <var>lflow</var> may be specified as
> +          a UUID or the first few characters of a UUID, optionally prefixed by
> +          <code>0x</code>.  (Because <code>ovn-controller</code> sets OpenFlow
> +          flow cookies to the first 32 bits of the corresponding logical flow's
> +          UUID, this makes it easy to look up the logical flow that generated a
> +          particular OpenFlow flow.)
> +        </p>
> +
> +        <p>
> +          If <code>--uuid</code> is specified, the output includes the first 32
> +          bits of each logical flow's UUID.  This makes it easier to find the
> +          OpenFlow flows that correspond to a given logical flow.
> +        </p>
> +
> +        <p>
> +          If <code>--ovs</code> is included, <code>ovn-sbctl</code> attempts to
> +          obtain and display the OpenFlow flows that correspond to each OVN
> +          logical flow.  To do so, <code>ovn-sbctl</code> connects to
> +          <var>remote</var> (by default,
> +          <code>unix:@RUNDIR@/br-int.mgmt</code>) over OpenFlow and retrieves
> +          the flows.  If <var>remote</var> is specified, it must be an active
> +          OpenFlow connection method described in <code>ovsdb</code>(7).
> +          Please see the discussion of the similar <code>--ovs</code> option in
> +          <code>ovn-trace</code>(8) for more information about the OpenFlow
> +          flow output.
> +        </p>
> +
> +        <p>
> +          By default, OpenFlow flow output includes only match and actions.
> +          Add <code>--stats</code> to include all OpenFlow information, such as
> +          packet and byte counters, duration, and timeouts.
> +        </p>
> +
> +        <p>
> +          If <code>--vflows</code> is included, other southbound database
> +          records directly used for generating OpenFlow flows are also
> +          listed. This includes: <var>port-bindings</var>,
> +          <var>mac-bindings</var>, <var>multicast-groups</var>,
> +          <var>chassis</var>.  The <code>--ovs</code> and <code>--stats</code>
> +          can also be used in conjunction with <code>--vflows</code>.
> +        </p>
> +      </dd>
> +
> +      <dt>[<code>--uuid</code>] <code>dump-flows</code> [<var>logical-datapath</var>]</dt>
> +      <dd>Alias for <code>lflow-list</code>.</dd>
> +    </dl>
> +
> +    <h2>Remote Connectivity Commands</h2>
> +
> +    <p>
> +      These commands manipulate the <code>connections</code> column in the
> +      <code>SB_Global</code> table and rows in the <code>Connection</code>
> +      table.  When <code>ovsdb-server</code> is configured to use the
> +      <code>connections</code> column for OVSDB connections, this allows the
> +      administrator to use \fBovn\-sbctl\fR to configure database connections.
> +    </p>
> +
> +    <dl>
> +      <dt><code>get-connection</code></dt>
> +      <dd>
> +      Prints the configured connection(s).
> +      </dd>
> +
> +      <dt><code>del-connection</code></dt>
> +      <dd>
> +      Deletes the configured connection(s).
> +      </dd>
> +
> +      <dt>[<code>--inactivity-probe=</code><var>msecs</var>] <code>set-connection</code> <var>target</var>...</dt>
> +      <dd>
> +        Sets the configured manager target or targets.  Use
> +        <code>--inactivity-probe=</code><var>msecs</var> to override the
> +        default idle connection inactivity probe time.  Use 0 to disable
> +        inactivity probes.
> +      </dd>
> +    </dl>
> +
> +    <h2>SSL Configuration Commands</h2>
> +    <p>
> +      When <code>ovsdb-server</code> is configured to connect using SSL, the
> +      following parameters are required:
> +    </p>
> +
> +    <dl>
> +      <dt><var>private-key</var></dt>
> +      <dd>
> +        Specifies a PEM file containing the private key used for SSL
> +        connections.
> +      </dd>
> +
> +      <dt><var>certificate</var></dt>
> +      <dd>
> +        Specifies a PEM file containing a certificate, signed by the
> +        certificate authority (CA) used by the connection peers, that
> +        certifies the private key, identifying a trustworthy peer.
> +      </dd>
> +
> +      <dt><var>ca-cert</var></dt>
> +      <dd>
> +        Specifies a PEM file containing the CA certificate used to verify that
> +        the connection peers are trustworthy.
> +      </dd>
> +    </dl>
> +
> +    <p>
> +      These SSL settings apply to all SSL connections made by the southbound
> +      database server.
> +    </p>
> +
> +    <dl>
> +      <dt><code>get-ssl</code></dt>
> +      <dd>
> +      Prints the SSL configuration.
> +      </dd>
> +
> +      <dt><code>del-ssl</code></dt>
> +      <dd>
> +      Deletes the current SSL configuration.
> +      </dd>
> +
> +      <dt>[<code>--bootstrap</code>] <code>set-ssl</code>
> +         <var>private-key</var> <var>certificate</var> <var>ca-cert</var>
> +         [<var>ssl-protocol-list</var> [<var>ssl-cipher-list</var>]]</dt>
> +      <dd>
> +      Sets the SSL configuration.
> +      </dd>
> +    </dl>
> +
> +    <h2>Database Commands</h2>
> +    <p>
> +      These commands query and modify the contents of <code>ovsdb</code>
> +      tables.  They are a slight abstraction of the <code>ovsdb</code>
> +      interface and as such they operate at a lower level than other
> +      <code>ovn-sbctl</code> commands.
> +    </p>
> +
> +    <p><var>Identifying Tables, Records, and Columns</var></p>
> +
> +    <p>
> +      Each of these commands has a <var>table</var> parameter to identify a
> +      table within the database.  Many of them also take a <var>record</var>
> +      parameter that identifies a particular record within a table.  The
> +      <var>record</var> parameter may be the UUID for a record, which may be
> +      abbreviated to its first 4 (or more) hex digits, as long as that is
> +      unique.  Many tables offer additional ways to identify records.  Some
> +      commands also take <var>column</var> parameters that identify a
> +      particular field within the records in a table.
> +    </p>
> +
> +    <p>
> +      For a list of tables and their columns, see <code>ovn-sb</code>(5) or
> +      see the table listing from the <code>--help</code> option.
> +    </p>
> +
> +    <p>
> +      Record names must be specified in full and with correct capitalization,
> +      except that UUIDs may be abbreviated to their first 4 (or more) hex
> +      digits, as long as that is unique within the table.  Names of tables and
> +      columns are not case-sensitive, and <code>-</code> and <code>_</code> are
> +      treated interchangeably.  Unique abbreviations of table and column names
> +      are acceptable, e.g. <code>d</code> or <code>dhcp</code> is sufficient
> +      to identify the <code>DHCP_Options</code> table.
> +    </p>
> +
> +    <xi:include href="lib/db-ctl-base.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
> +
> +    <h1>Environment</h1>
> +
> +    <dl>
> +      <dt><env>OVN_SB_DAEMON</env></dt>
> +      <dd>
> +        If set, this should name the Unix domain socket for an
> +        <code>ovn-sbctl</code> server process.  See <code>Daemon Mode</code>,
> +        above, for more information.
> +      </dd>
> +
> +      <dt><env>OVN_SBCTL_OPTIONS</env></dt>
> +      <dd>
> +        If set, a set of options for <code>ovn-sbctl</code> to apply
> +        automatically, in the same form as on the command line.
> +      </dd>
> +
> +      <dt><env>OVN_SB_DB</env></dt>
> +      <dd>
> +        If set, the default database to contact when the <code>--db</code>
> +        option is not used.
> +      </dd>
> +    </dl>
> +
> +    <h1>Exit Status</h1>
> +    <dl>
> +      <dt>0</dt>
> +      <dd>Successful program execution.</dd>
> +
> +      <dt>1</dt>
> +      <dd>Usage, syntax, or network error.</dd>
> +    </dl>
> +
> +    <h1>See Also</h1>
> +    <code>ovn-sb</code>(5).
> +
> +</manpage>
> diff --git a/utilities/ovn-sbctl.c b/utilities/ovn-sbctl.c
> index e3aa7a68e680..f1bd2b417e56 100644
> --- a/utilities/ovn-sbctl.c
> +++ b/utilities/ovn-sbctl.c
> @@ -31,8 +31,10 @@
>   #include "command-line.h"
>   #include "compiler.h"
>   #include "db-ctl-base.h"
> +#include "daemon.h"
>   #include "dirs.h"
>   #include "fatal-signal.h"
> +#include "jsonrpc.h"
>   #include "openvswitch/dynamic-string.h"
>   #include "openvswitch/json.h"
>   #include "openvswitch/ofp-actions.h"
> @@ -43,276 +45,45 @@
>   #include "openvswitch/vlog.h"
>   #include "lib/ovn-sb-idl.h"
>   #include "lib/ovn-util.h"
> +#include "memory.h"
> +#include "ovn-dbctl.h"
>   #include "ovsdb-data.h"
>   #include "ovsdb-idl.h"
>   #include "openvswitch/poll-loop.h"
>   #include "process.h"
> +#include "simap.h"
>   #include "sset.h"
>   #include "stream-ssl.h"
>   #include "stream.h"
>   #include "table.h"
> +#include "timer.h"
>   #include "timeval.h"
> +#include "unixctl.h"
>   #include "util.h"
>   #include "svec.h"
>   
>   VLOG_DEFINE_THIS_MODULE(sbctl);
>   
> -struct sbctl_context;
> -
> -/* --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;
> -
> -/* --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 sbctl_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 sbctl_exit(int status);
> -
> -/* --leader-only, --no-leader-only: Only accept the leader in a cluster. */
> -static int leader_only = true;
> -
> -static void sbctl_cmd_init(void);
> -OVS_NO_RETURN static void usage(void);
> -static void parse_options(int argc, char *argv[], struct shash *local_options);
> -static void run_prerequisites(struct ctl_command[], size_t n_commands,
> -                              struct ovsdb_idl *);
> -static bool do_sbctl(const char *args, struct ctl_command *, size_t n,
> -                     struct ovsdb_idl *);
> -
> -int
> -main(int argc, char *argv[])
> +static void
> +sbctl_add_base_prerequisites(struct ovsdb_idl *idl,
> +                             enum nbctl_wait_type wait_type OVS_UNUSED)
>   {
> -    struct ovsdb_idl *idl;
> -    struct ctl_command *commands;
> -    struct shash local_options;
> -    unsigned int seqno;
> -    size_t n_commands;
> -
> -    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");
> -
> -    sbctl_cmd_init();
> -
> -    /* Check if options are set via env var. */
> -    char **argv_ = ovs_cmdl_env_parse_all(&argc, argv,
> -                                          getenv("OVN_SBCTL_OPTIONS"));
> -
> -    /* Parse command line. */
> -    char *args = process_escape_args(argv_);
> -    shash_init(&local_options);
> -    parse_options(argc, argv_, &local_options);
> -    char *error = ctl_parse_commands(argc - optind, argv_ + optind,
> -                                     &local_options, &commands, &n_commands);
> -    if (error) {
> -        ctl_fatal("%s", error);
> -    }
> -    VLOG(ctl_might_write_to_db(commands, n_commands) ? VLL_INFO : VLL_DBG,
> -         "Called as %s", args);
> -
> -    ctl_timeout_setup(timeout);
> -
> -    /* Initialize IDL. */
> -    idl = the_idl = ovsdb_idl_create(db, &sbrec_idl_class, false, true);
> -    ovsdb_idl_set_leader_only(idl, leader_only);
> -    run_prerequisites(commands, n_commands, idl);
> -
> -    /* 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);
> -    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 (seqno != ovsdb_idl_get_seqno(idl)) {
> -            seqno = ovsdb_idl_get_seqno(idl);
> -            if (do_sbctl(args, commands, n_commands, idl)) {
> -                break;
> -            }
> -        }
> -
> -        if (seqno == ovsdb_idl_get_seqno(idl)) {
> -            ovsdb_idl_wait(idl);
> -            poll_block();
> -        }
> -    }
> -
> -    for (int i = 0; i < argc; i++) {
> -        free(argv_[i]);
> -    }
> -    free(argv_);
> -    free(args);
> -    exit(EXIT_SUCCESS);
> +    ovsdb_idl_add_table(idl, &sbrec_table_sb_global);
>   }
>   
>   static void
> -parse_options(int argc, char *argv[], struct shash *local_options)
> +sbctl_pre_execute(struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn,
> +                  enum nbctl_wait_type wait_type OVS_UNUSED)
>   {
> -    enum {
> -        OPT_DB = UCHAR_MAX + 1,
> -        OPT_ONELINE,
> -        OPT_NO_SYSLOG,
> -        OPT_DRY_RUN,
> -        OPT_LOCAL,
> -        OPT_COMMANDS,
> -        OPT_OPTIONS,
> -        OPT_BOOTSTRAP_CA_CERT,
> -        VLOG_OPTION_ENUMS,
> -        TABLE_OPTION_ENUMS,
> -        SSL_OPTION_ENUMS,
> -    };
> -    static const struct option global_long_options[] = {
> -        {"db", required_argument, NULL, OPT_DB},
> -        {"no-syslog", no_argument, NULL, OPT_NO_SYSLOG},
> -        {"dry-run", no_argument, NULL, OPT_DRY_RUN},
> -        {"oneline", no_argument, NULL, OPT_ONELINE},
> -        {"timeout", required_argument, NULL, 't'},
> -        {"help", no_argument, NULL, 'h'},
> -        {"commands", no_argument, NULL, OPT_COMMANDS},
> -        {"options", no_argument, NULL, OPT_OPTIONS},
> -        {"leader-only", no_argument, &leader_only, true},
> -        {"no-leader-only", no_argument, &leader_only, false},
> -        {"version", no_argument, NULL, 'V'},
> -        VLOG_LONG_OPTIONS,
> -        STREAM_SSL_LONG_OPTIONS,
> -        {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
> -        TABLE_LONG_OPTIONS,
> -        {NULL, 0, NULL, 0},
> -    };
> -    const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1;
> -    char *tmp, *short_options;
> -
> -    struct option *options;
> -    size_t allocated_options;
> -    size_t n_options;
> -    size_t i;
> -
> -    tmp = ovs_cmdl_long_options_to_short_options(global_long_options);
> -    short_options = xasprintf("+%s", tmp);
> -    free(tmp);
> -
> -    /* 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. */
> -    options = xmemdup(global_long_options, sizeof global_long_options);
> -    allocated_options = ARRAY_SIZE(global_long_options);
> -    n_options = n_global_long_options;
> -    ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL);
> -
> -    for (;;) {
> -        int idx;
> -        int c;
> -
> -        c = getopt_long(argc, argv, short_options, options, &idx);
> -        if (c == -1) {
> -            break;
> -        }
> -
> -        switch (c) {
> -        case OPT_DB:
> -            db = optarg;
> -            break;
> -
> -        case OPT_ONELINE:
> -            oneline = true;
> -            break;
> -
> -        case OPT_NO_SYSLOG:
> -            vlog_set_levels(&this_module, VLF_SYSLOG, VLL_WARN);
> -            break;
> -
> -        case OPT_DRY_RUN:
> -            dry_run = true;
> -            break;
> -
> -        case OPT_LOCAL:
> -            if (shash_find(local_options, options[idx].name)) {
> -                ctl_fatal("'%s' option specified multiple times",
> -                            options[idx].name);
> -            }
> -            shash_add_nocopy(local_options,
> -                             xasprintf("--%s", options[idx].name),
> -                             nullable_xstrdup(optarg));
> -            break;
> -
> -        case 'h':
> -            usage();
> -
> -        case OPT_COMMANDS:
> -            ctl_print_commands();
> -            /* fall through */
> -
> -        case OPT_OPTIONS:
> -            ctl_print_options(global_long_options);
> -            /* fall through */
> -
> -        case 'V':
> -            ovn_print_version(0, 0);
> -            printf("DB Schema %s\n", sbrec_get_db_version());
> -            exit(EXIT_SUCCESS);
> -
> -        case 't':
> -            if (!str_to_uint(optarg, 10, &timeout) || !timeout) {
> -                ctl_fatal("value %s on -t or --timeout is invalid", optarg);
> -            }
> -            break;
> -
> -        VLOG_OPTION_HANDLERS
> -        TABLE_OPTION_HANDLERS(&table_style)
> -        STREAM_SSL_OPTION_HANDLERS
> -
> -        case OPT_BOOTSTRAP_CA_CERT:
> -            stream_ssl_set_ca_cert_file(optarg, true);
> -            break;
> -
> -        case '?':
> -            exit(EXIT_FAILURE);
> -
> -        default:
> -            abort();
> -
> -        case 0:
> -            break;
> -        }
> -    }
> -    free(short_options);
> -
> -    if (!db) {
> -        db = default_sb_db();
> -    }
> -
> -    for (i = n_global_long_options; options[i].name; i++) {
> -        free(CONST_CAST(char *, options[i].name));
> +    const struct sbrec_sb_global *sb = sbrec_sb_global_first(idl);
> +    if (!sb) {
> +        /* XXX add verification that table is empty */
> +        sb = sbrec_sb_global_insert(txn);
>       }
> -    free(options);
>   }
>   
>   static void
> -usage(void)
> +sbctl_usage(void)
>   {
>       printf("\
>   %s: OVN southbound DB management utility\n\
> @@ -372,8 +143,12 @@ Other options:\n\
>       stream_usage("database", true, true, true);
>       exit(EXIT_SUCCESS);
>   }
> -
>   

> +/* One should not use ctl_fatal() within commands because it will kill the
> + * daemon if we're in daemon mode.  Use ctl_error() instead and return
> + * gracefully.  */
> +#define ctl_fatal dont_use_ctl_fatal_use_ctl_error_and_return
> +
>   /* ovs-sbctl specific context.  Inherits the 'struct ctl_context' as base. */
>   struct sbctl_context {
>       struct ctl_context base;
> @@ -420,18 +195,20 @@ sbctl_context_invalidate_cache(struct ctl_context *ctx)
>       shash_destroy_free_data(&sbctl_ctx->port_bindings);
>   }
>   
> -static void
> -sbctl_context_populate_cache(struct ctl_context *ctx)
> +/* Casts 'base' into 'struct sbctl_context' and initializes it if needed. */
> +static struct sbctl_context *
> +sbctl_context_get(struct ctl_context *ctx)
>   {
> -    struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
> +    struct sbctl_context *sbctl_ctx
> +        = CONTAINER_OF(ctx, struct sbctl_context, base);
> +    if (sbctl_ctx->cache_valid) {
> +        return sbctl_ctx;
> +    }
> +
>       const struct sbrec_chassis *chassis_rec;
>       const struct sbrec_port_binding *port_binding_rec;
>       struct sset chassis, port_bindings;
>   
> -    if (sbctl_ctx->cache_valid) {
> -        /* Cache is already populated. */
> -        return;
> -    }
>       sbctl_ctx->cache_valid = true;
>       shash_init(&sbctl_ctx->chassis);
>       shash_init(&sbctl_ctx->port_bindings);
> @@ -468,46 +245,63 @@ sbctl_context_populate_cache(struct ctl_context *ctx)
>                     bd);
>       }
>       sset_destroy(&port_bindings);
> +
> +    return sbctl_ctx;
> +}
> +
> +static struct ctl_context *
> +sbctl_ctx_create(void)
> +{
> +    struct sbctl_context *sbctx = xmalloc(sizeof *sbctx);
> +    *sbctx = (struct sbctl_context) {
> +        .cache_valid = false,
> +    };
> +    return &sbctx->base;
>   }
>   
>   static void
> -check_conflicts(struct sbctl_context *sbctl_ctx, const char *name,
> -                char *msg)
> +sbctl_ctx_destroy(struct ctl_context *ctx)
>   {
> +    sbctl_context_invalidate_cache(ctx);
> +    free(ctx);
> +}
> +
> +static bool
> +check_conflicts(struct ctl_context *ctx, const char *name, char *msg)
> +{
> +    struct sbctl_context *sbctl_ctx = sbctl_context_get(ctx);
>       if (shash_find(&sbctl_ctx->chassis, name)) {
> -        ctl_fatal("%s because a chassis named %s already exists",
> -                    msg, name);
> +        ctl_error(&sbctl_ctx->base,
> +                  "%s because a chassis named %s already exists",
> +                  msg, name);
> +        return false;
>       }
>       free(msg);
> +
> +    return true;
>   }
>   
>   static struct sbctl_chassis *
> -find_chassis(struct sbctl_context *sbctl_ctx, const char *name,
> -             bool must_exist)
> +find_chassis(struct ctl_context *ctx, const char *name, bool must_exist)
>   {
> -    struct sbctl_chassis *sbctl_ch;
> -
> -    ovs_assert(sbctl_ctx->cache_valid);
> -
> -    sbctl_ch = shash_find_data(&sbctl_ctx->chassis, name);
> +    struct sbctl_context *sbctl_ctx = sbctl_context_get(ctx);
> +    struct sbctl_chassis *sbctl_ch = shash_find_data(&sbctl_ctx->chassis,
> +                                                     name);
>       if (must_exist && !sbctl_ch) {
> -        ctl_fatal("no chassis named %s", name);
> +        ctl_error(ctx, "no chassis named %s", name);
>       }
>   
>       return sbctl_ch;
>   }
>   
>   static struct sbctl_port_binding *
> -find_port_binding(struct sbctl_context *sbctl_ctx, const char *name,
> -                  bool must_exist)
> +find_port_binding(struct ctl_context *ctx, const char *name, bool must_exist)
>   {
> -    struct sbctl_port_binding *bd;
> -
> -    ovs_assert(sbctl_ctx->cache_valid);
> -
> -    bd = shash_find_data(&sbctl_ctx->port_bindings, name);
> +    struct sbctl_context *sbctl_ctx = sbctl_context_get(ctx);
> +    struct sbctl_port_binding *bd = shash_find_data(&sbctl_ctx->port_bindings,
> +                                                    name);
>       if (must_exist && !bd) {
> -        ctl_fatal("no port named %s", name);
> +        ctl_error(&sbctl_ctx->base, "no port named %s", name);
>       }
>   
>       return bd;
> @@ -588,7 +382,6 @@ sbctl_init(struct ctl_context *ctx OVS_UNUSED)
>   static void
>   cmd_chassis_add(struct ctl_context *ctx)
>   {
> -    struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
>       bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
>       const char *ch_name, *encap_types, *encap_ip;
>   
> @@ -596,17 +389,17 @@ cmd_chassis_add(struct ctl_context *ctx)
>       encap_types = ctx->argv[2];
>       encap_ip = ctx->argv[3];
>   
> -    sbctl_context_populate_cache(ctx);
>       if (may_exist) {
> -        struct sbctl_chassis *sbctl_ch;
> -
> -        sbctl_ch = find_chassis(sbctl_ctx, ch_name, false);
> +        struct sbctl_chassis *sbctl_ch = find_chassis(ctx, ch_name, false);
>           if (sbctl_ch) {
>               return;
>           }
>       }
> -    check_conflicts(sbctl_ctx, ch_name,
> -                    xasprintf("cannot create a chassis named %s", ch_name));
> +    if (!check_conflicts(ctx, ch_name,
> +                         xasprintf("cannot create a chassis named %s",
> +                                   ch_name))) {
> +        return;
> +    }
>   
>       struct sset encap_set;
>       sset_from_delimited_string(&encap_set, encap_types, ",");
> @@ -642,8 +435,7 @@ cmd_chassis_del(struct ctl_context *ctx)
>       bool must_exist = !shash_find(&ctx->options, "--if-exists");
>       struct sbctl_chassis *sbctl_ch;
>   
> -    sbctl_context_populate_cache(ctx);
> -    sbctl_ch = find_chassis(sbctl_ctx, ctx->argv[1], must_exist);
> +    sbctl_ch = find_chassis(ctx, ctx->argv[1], must_exist);
>       if (sbctl_ch) {
>           if (sbctl_ch->ch_cfg) {
>               size_t i;
> @@ -672,17 +464,21 @@ cmd_lsp_bind(struct ctl_context *ctx)
>       lport_name = ctx->argv[1];
>       ch_name = ctx->argv[2];
>   
> -    sbctl_context_populate_cache(ctx);
> -    sbctl_bd = find_port_binding(sbctl_ctx, lport_name, true);
> -    sbctl_ch = find_chassis(sbctl_ctx, ch_name, true);
> +    sbctl_bd = find_port_binding(ctx, lport_name, true);
> +    if (!sbctl_ctx) {
> +        return;
> +    }
> +    sbctl_ch = find_chassis(ctx, ch_name, true);
> +    if (!sbctl_ch) {
> +        return;
> +    }
>   
>       if (sbctl_bd->bd_cfg->chassis) {
> -        if (may_exist && sbctl_bd->bd_cfg->chassis == sbctl_ch->ch_cfg) {
> -            return;
> -        } else {
> -            ctl_fatal("lport (%s) has already been binded to chassis (%s)",
> +        if (!may_exist || sbctl_bd->bd_cfg->chassis != sbctl_ch->ch_cfg) {
> +            ctl_error(ctx, "lport (%s) has already been binded to chassis (%s)",
>                         lport_name, sbctl_bd->bd_cfg->chassis->name);
>           }
> +        return;
>       }
>       sbrec_port_binding_set_chassis(sbctl_bd->bd_cfg, sbctl_ch->ch_cfg);
>       sbrec_port_binding_set_up(sbctl_bd->bd_cfg, &up, 1);
> @@ -692,14 +488,12 @@ cmd_lsp_bind(struct ctl_context *ctx)
>   static void
>   cmd_lsp_unbind(struct ctl_context *ctx)
>   {
> -    struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
>       bool must_exist = !shash_find(&ctx->options, "--if-exists");
>       struct sbctl_port_binding *sbctl_bd;
>       char *lport_name;
>   
>       lport_name = ctx->argv[1];
> -    sbctl_context_populate_cache(ctx);
> -    sbctl_bd = find_port_binding(sbctl_ctx, lport_name, must_exist);
> +    sbctl_bd = find_port_binding(ctx, lport_name, must_exist);
>       if (sbctl_bd) {
>           sbrec_port_binding_set_chassis(sbctl_bd->bd_cfg, NULL);
>           sbrec_port_binding_set_up(sbctl_bd->bd_cfg, NULL, 0);
> @@ -1123,7 +917,9 @@ cmd_lflow_list(struct ctl_context *ctx)
>           char *error = ctl_get_row(ctx, &sbrec_table_datapath_binding,
>                                     ctx->argv[1], false, &row);
>           if (error) {
> -            ctl_fatal("%s", error);
> +            ctl_error(ctx, "%s", error);
> +            free(error);
> +            return;
>           }
>   
>           datapath = (const struct sbrec_datapath_binding *)row;
> @@ -1136,8 +932,9 @@ cmd_lflow_list(struct ctl_context *ctx)
>       for (size_t i = 1; i < ctx->argc; i++) {
>           char *s = parse_partial_uuid(ctx->argv[i]);
>           if (!s) {
> -            ctl_fatal("%s is not a UUID or the beginning of a UUID",
> +            ctl_error(ctx, "%s is not a UUID or the beginning of a UUID",
>                         ctx->argv[i]);
> +            return;
>           }
>           ctx->argv[i] = s;
>       }
> @@ -1272,12 +1069,15 @@ sbctl_ip_mcast_flush(struct ctl_context *ctx)
>           char *error = ctl_get_row(ctx, &sbrec_table_datapath_binding,
>                                     ctx->argv[1], false, &row);
>           if (error) {
> -            ctl_fatal("%s", error);
> +            ctl_error(ctx, "%s", error);
> +            free(error);
> +            return;
>           }
>   
>           dp = (const struct sbrec_datapath_binding *)row;
>           if (!dp) {
> -            ctl_fatal("%s is not a valid datapath", ctx->argv[1]);
> +            ctl_error(ctx, "%s is not a valid datapath", ctx->argv[1]);
> +            return;
>           }
>   
>           sbctl_ip_mcast_flush_switch(ctx, dp);
> @@ -1564,248 +1364,6 @@ static const struct ctl_table_class tables[SBREC_N_TABLES] = {
>       = {&sbrec_load_balancer_col_name, NULL, NULL},
>   };
>   
> -

> -static void
> -sbctl_context_init_command(struct sbctl_context *sbctl_ctx,
> -                           struct ctl_command *command)
> -{
> -    ctl_context_init_command(&sbctl_ctx->base, command);
> -}
> -
> -static void
> -sbctl_context_init(struct sbctl_context *sbctl_ctx,
> -                   struct ctl_command *command, struct ovsdb_idl *idl,
> -                   struct ovsdb_idl_txn *txn,
> -                   struct ovsdb_symbol_table *symtab)
> -{
> -    ctl_context_init(&sbctl_ctx->base, command, idl, txn, symtab,
> -                     sbctl_context_invalidate_cache);
> -    sbctl_ctx->cache_valid = false;
> -}
> -
> -static void
> -sbctl_context_done_command(struct sbctl_context *sbctl_ctx,
> -                           struct ctl_command *command)
> -{
> -    ctl_context_done_command(&sbctl_ctx->base, command);
> -}
> -
> -static void
> -sbctl_context_done(struct sbctl_context *sbctl_ctx,
> -                   struct ctl_command *command)
> -{
> -    ctl_context_done(&sbctl_ctx->base, command);
> -}
> -
> -static void
> -run_prerequisites(struct ctl_command *commands, size_t n_commands,
> -                  struct ovsdb_idl *idl)
> -{
> -    ovsdb_idl_add_table(idl, &sbrec_table_sb_global);
> -
> -    for (struct ctl_command *c = commands; c < &commands[n_commands]; c++) {
> -        if (c->syntax->prerequisites) {
> -            struct sbctl_context sbctl_ctx;
> -
> -            ds_init(&c->output);
> -            c->table = NULL;
> -
> -            sbctl_context_init(&sbctl_ctx, c, idl, NULL, NULL);
> -            (c->syntax->prerequisites)(&sbctl_ctx.base);
> -            if (sbctl_ctx.base.error) {
> -                ctl_fatal("%s", sbctl_ctx.base.error);
> -            }
> -            sbctl_context_done(&sbctl_ctx, c);
> -
> -            ovs_assert(!c->output.string);
> -            ovs_assert(!c->table);
> -        }
> -    }
> -}
> -
> -static bool
> -do_sbctl(const char *args, struct ctl_command *commands, size_t n_commands,
> -         struct ovsdb_idl *idl)
> -{
> -    struct ovsdb_idl_txn *txn;
> -    enum ovsdb_idl_txn_status status;
> -    struct ovsdb_symbol_table *symtab;
> -    struct sbctl_context sbctl_ctx;
> -    struct ctl_command *c;
> -    struct shash_node *node;
> -
> -    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-sbctl: %s", args);
> -
> -    const struct sbrec_sb_global *sb = sbrec_sb_global_first(idl);
> -    if (!sb) {
> -        /* XXX add verification that table is empty */
> -        sbrec_sb_global_insert(txn);
> -    }
> -
> -    symtab = ovsdb_symbol_table_create();
> -    for (c = commands; c < &commands[n_commands]; c++) {
> -        ds_init(&c->output);
> -        c->table = NULL;
> -    }
> -    sbctl_context_init(&sbctl_ctx, NULL, idl, txn, symtab);
> -    for (c = commands; c < &commands[n_commands]; c++) {
> -        sbctl_context_init_command(&sbctl_ctx, c);
> -        if (c->syntax->run) {
> -            (c->syntax->run)(&sbctl_ctx.base);
> -        }
> -        if (sbctl_ctx.base.error) {
> -            ctl_fatal("%s", sbctl_ctx.base.error);
> -        }
> -        sbctl_context_done_command(&sbctl_ctx, c);
> -
> -        if (sbctl_ctx.base.try_again) {
> -            sbctl_context_done(&sbctl_ctx, NULL);
> -            goto try_again;
> -        }
> -    }
> -    sbctl_context_done(&sbctl_ctx, NULL);
> -
> -    SHASH_FOR_EACH (node, &symtab->sh) {
> -        struct ovsdb_symbol *symbol = node->data;
> -        if (!symbol->created) {
> -            ctl_fatal("row id \"%s\" is referenced but never created (e.g. "
> -                      "with \"-- --id=%s create ...\")",
> -                      node->name, node->name);
> -        }
> -        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);
> -            }
> -        }
> -    }
> -
> -    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) {
> -                sbctl_context_init(&sbctl_ctx, c, idl, txn, symtab);
> -                (c->syntax->postprocess)(&sbctl_ctx.base);
> -                if (sbctl_ctx.base.error) {
> -                    ctl_fatal("%s", sbctl_ctx.base.error);
> -                }
> -                sbctl_context_done(&sbctl_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(). */
> -        ctl_fatal("transaction aborted");
> -
> -    case TXN_UNCHANGED:
> -    case TXN_SUCCESS:
> -        break;
> -
> -    case TXN_TRY_AGAIN:
> -        goto try_again;
> -
> -    case TXN_ERROR:
> -        ctl_fatal("transaction error: %s", ovsdb_idl_txn_get_error(txn));
> -
> -    case TXN_NOT_LOCKED:
> -        /* Should not happen--we never call ovsdb_idl_set_lock(). */
> -        ctl_fatal("database not locked");
> -
> -    default:
> -        OVS_NOT_REACHED();
> -    }
> -
> -    ovsdb_symbol_table_destroy(symtab);
> -
> -    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) {
> -            size_t j;
> -
> -            ds_chomp(ds, '\n');
> -            for (j = 0; j < ds->length; j++) {
> -                int ch = ds->string[j];
> -                switch (ch) {
> -                case '\n':
> -                    fputs("\\n", stdout);
> -                    break;
> -
> -                case '\\':
> -                    fputs("\\\\", stdout);
> -                    break;
> -
> -                default:
> -                    putchar(ch);
> -                }
> -            }
> -            putchar('\n');
> -        } else {
> -            fputs(ds_cstr(ds), stdout);
> -        }
> -        ds_destroy(&c->output);
> -        table_destroy(c->table);
> -        free(c->table);
> -
> -        shash_destroy_free_data(&c->options);
> -    }
> -    free(commands);
> -    ovsdb_idl_txn_destroy(txn);
> -    ovsdb_idl_destroy(idl);
> -
> -    return true;
> -
> -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. */
> -    ovsdb_idl_txn_abort(txn);
> -    ovsdb_idl_txn_destroy(txn);
> -    the_idl_txn = NULL;
> -
> -    ovsdb_symbol_table_destroy(symtab);
> -    for (c = commands; c < &commands[n_commands]; c++) {
> -        ds_destroy(&c->output);
> -        table_destroy(c->table);
> -        free(c->table);
> -    }
> -    return false;
> -}
> -
> -/* 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
> -sbctl_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 sbctl_commands[] = {
>       { "init", 0, 0, "", NULL, sbctl_init, NULL, "", RW },
>   
> @@ -1850,11 +1408,31 @@ static const struct ctl_command_syntax sbctl_commands[] = {
>       {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO},
>   };
>   
> -/* Registers sbctl and common db commands. */
> -static void
> -sbctl_cmd_init(void)
> +int
> +main(int argc, char *argv[])
>   {
> -    ctl_init(&sbrec_idl_class, sbrec_table_classes, tables,
> -             cmd_show_tables, sbctl_exit);
> -    ctl_register_commands(sbctl_commands);
> +    struct ovn_dbctl_options dbctl_options = {
> +        .db_version = sbrec_get_db_version(),
> +        .default_db = default_sb_db(),
> +        .allow_wait = false,
> +
> +        .options_env_var_name = "OVN_SBCTL_OPTIONS",
> +        .daemon_env_var_name = "OVN_SBCTL_DAEMON",
> +
> +        .idl_class = &sbrec_idl_class,
> +        .tables = tables,
> +        .cmd_show_table = cmd_show_tables,
> +        .commands = sbctl_commands,
> +
> +        .usage = sbctl_usage,
> +        .add_base_prerequisites = sbctl_add_base_prerequisites,
> +        .pre_execute = sbctl_pre_execute,
> +        .post_execute = NULL,
> +
> +        .ctx_create = sbctl_ctx_create,
> +        .ctx_destroy = sbctl_ctx_destroy,
> +    };
> +
> +    return ovn_dbctl_main(argc, argv, &dbctl_options);
>   }
> +
> 



More information about the dev mailing list