[ovs-dev] [PATCH ovn v3 08/27] ovn-sbctl: Add daemon support.
Ben Pfaff
blp at ovn.org
Fri May 7 04:06:40 UTC 2021
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 | 4 +-
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 | 580 +++++++++++++++++++++++++++++++++
utilities/ovn-sbctl.c | 669 +++++++-------------------------------
10 files changed, 776 insertions(+), 922 deletions(-)
delete mode 100644 utilities/ovn-sbctl.8.in
create mode 100644 utilities/ovn-sbctl.8.xml
diff --git a/NEWS b/NEWS
index 0565a0975d6e..67c8b5f8bb38 100644
--- a/NEWS
+++ b/NEWS
@@ -16,7 +16,9 @@ Post-v21.03.0
be used in the logical flow matches. CMS can consider setting this to
false, if they want to use smart NICs which don't support offloading
datapath flows with this field used.
- - ovn-nbctl daemon mode is no longer considered experimental.
+ - Utilities:
+ * ovn-nbctl daemon mode is no longer considered experimental.
+ * ovn-sbctl now also supports daemon mode.
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 d815dc5c8c5f..ffe85ce6d5d9 100644
--- a/utilities/ovn-dbctl.c
+++ b/utilities/ovn-dbctl.c
@@ -327,7 +327,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;
@@ -338,11 +339,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;
@@ -355,6 +361,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;
@@ -486,7 +495,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);
}
@@ -834,7 +844,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[] = {
@@ -865,7 +876,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;
}
@@ -967,7 +978,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 14840a8fa074..7ae7dcfc4607 100644
--- a/utilities/ovn-nbctl.c
+++ b/utilities/ovn-nbctl.c
@@ -5986,6 +5986,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_NB_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..4e6b21c47369
--- /dev/null
+++ b/utilities/ovn-sbctl.8.xml
@@ -0,0 +1,580 @@
+<?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>
+
+ <h3>Daemon Commands</h3>
+
+ <p>
+ Daemon mode is internally implemented using the same mechanism used by
+ <code>ovn-appctl</code>. One may also use <code>ovn-appctl</code>
+ 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),
+ <code>ovn-appctl</code>(8).
+
+</manpage>
diff --git a/utilities/ovn-sbctl.c b/utilities/ovn-sbctl.c
index e3aa7a68e680..8cccc93f8dfe 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,48 @@ 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)
{
- if (shash_find(&sbctl_ctx->chassis, name)) {
- ctl_fatal("%s because a chassis named %s already exists",
- msg, name);
- }
- free(msg);
+ sbctl_context_invalidate_cache(ctx);
+ free(ctx);
}
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 +367,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 +374,20 @@ 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);
- if (sbctl_ch) {
+ if (find_chassis(ctx, ch_name, false)) {
+ if (may_exist) {
return;
}
}
- check_conflicts(sbctl_ctx, ch_name,
- xasprintf("cannot create a chassis named %s", ch_name));
+
+ struct sbctl_context *sbctl_ctx = sbctl_context_get(ctx);
+ if (shash_find(&sbctl_ctx->chassis, ch_name)) {
+ if (!may_exist) {
+ ctl_error(ctx, "cannot create a chassis named %s because a "
+ "chassis named %s already exists", ch_name, ch_name);
+ }
+ return;
+ }
struct sset encap_set;
sset_from_delimited_string(&encap_set, encap_types, ",");
@@ -642,8 +423,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;
@@ -661,7 +441,6 @@ cmd_chassis_del(struct ctl_context *ctx)
static void
cmd_lsp_bind(struct ctl_context *ctx)
{
- struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
struct sbctl_chassis *sbctl_ch;
struct sbctl_port_binding *sbctl_bd;
@@ -672,17 +451,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_bd) {
+ 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 +475,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 +904,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 +919,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 +1056,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 +1351,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 +1395,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_SB_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);
}
+
--
2.31.1
More information about the dev
mailing list