Message ID | 20210401232108.3902274-9-blp@ovn.org |
---|---|
State | Not Applicable |
Headers | show |
Series | ddlog 5x performance improvement | expand |
On 4/1/21 7:20 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@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 | 670 +++++++------------------------------- > 10 files changed, 783 insertions(+), 916 deletions(-) > delete mode 100644 utilities/ovn-sbctl.8.in > create mode 100644 utilities/ovn-sbctl.8.xml > > diff --git a/NEWS b/NEWS > index 8b170bcba6fb..a98529ac4ebe 100644 > --- a/NEWS > +++ b/NEWS > @@ -7,7 +7,9 @@ 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-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 42841bfb8890..ff7438a49d4b 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_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..197504fe4015 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); I think this branch should free msg as well. It's more important in daemon mode since the program isn't exiting immediately in this case. Since check_conflicts won't spontaneously exit now, it probably would make things easier for the caller of check_conflicts() to be responsible for freeing msg. This way, msg is allocated and freed in the same scope, and it gives the option of using a non dynamically allocated msg if desired. > + 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) { Should this be 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 +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_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); > } > + >
On Fri, Apr 16, 2021 at 12:05:12PM -0400, Mark Michelson wrote: > On 4/1/21 7:20 PM, Ben Pfaff wrote: > > +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); > > I think this branch should free msg as well. It's more important in daemon > mode since the program isn't exiting immediately in this case. > > Since check_conflicts won't spontaneously exit now, it probably would make > things easier for the caller of check_conflicts() to be responsible for > freeing msg. This way, msg is allocated and freed in the same scope, and it > gives the option of using a non dynamically allocated msg if desired. Thanks, good points. It turned out check_conflicts() was only used in one place and that the code got nicer if we just inlined it, like this: @@ -266,21 +266,6 @@ sbctl_ctx_destroy(struct ctl_context *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_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 ctl_context *ctx, const char *name, bool must_exist) { @@ -389,15 +374,18 @@ cmd_chassis_add(struct ctl_context *ctx) encap_types = ctx->argv[2]; encap_ip = ctx->argv[3]; - if (may_exist) { - struct sbctl_chassis *sbctl_ch = find_chassis(ctx, ch_name, false); - if (sbctl_ch) { + if (find_chassis(ctx, ch_name, false)) { + if (may_exist) { return; } } - if (!check_conflicts(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; } > > @@ -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) { > > Should this be > > if (!sbctl_bd) { > > ? Yes, thanks, fixed.
diff --git a/NEWS b/NEWS index 8b170bcba6fb..a98529ac4ebe 100644 --- a/NEWS +++ b/NEWS @@ -7,7 +7,9 @@ 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-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 42841bfb8890..ff7438a49d4b 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_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..197504fe4015 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_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); } +
Also rewrite the manpage and convert it to XML for consistency with ovn-nbctl, and add tests. Signed-off-by: Ben Pfaff <blp@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 | 670 +++++++------------------------------- 10 files changed, 783 insertions(+), 916 deletions(-) delete mode 100644 utilities/ovn-sbctl.8.in create mode 100644 utilities/ovn-sbctl.8.xml