[ovs-dev] [ovn-controller-vtep V3 3/7] ovn-sbctl: Add ovn-sbctl.
Alex Wang
alexw at nicira.com
Tue Jul 14 03:22:39 UTC 2015
This commit adds ovn-sbctl to ovn family by using the db-ctl-base
library.
As a side effect, we move the ovn-nbctl/ovn-sbctl related files
into ovn/utilities.
Signed-off-by: Alex Wang <alexw at nicira.com>
Acked-by: Ben Pfaff <blp at nicira.com>
---
V2->V3:
- rebase to base.
- adopt Russell's review comments.
- move ovn-nbctl/sbctl related files into ovn/utilities.
PATCH->V2:
- change command add/del-ch to add/del-chassis.
- refine the manual based on comments from Ben.
---
manpages.mk | 12 +
ovn/.gitignore | 2 -
ovn/automake.mk | 11 +-
ovn/utilities/.gitignore | 5 +
ovn/utilities/automake.mk | 18 +
ovn/{ => utilities}/ovn-nbctl.8.xml | 0
ovn/{ => utilities}/ovn-nbctl.c | 0
ovn/utilities/ovn-sbctl.8.in | 160 +++++++
ovn/utilities/ovn-sbctl.c | 835 +++++++++++++++++++++++++++++++++++
tests/automake.mk | 5 +-
tests/ovn-sbctl.at | 61 +++
tests/testsuite.at | 1 +
12 files changed, 1098 insertions(+), 12 deletions(-)
rename ovn/{ => utilities}/ovn-nbctl.8.xml (100%)
rename ovn/{ => utilities}/ovn-nbctl.c (100%)
create mode 100644 ovn/utilities/ovn-sbctl.8.in
create mode 100644 ovn/utilities/ovn-sbctl.c
create mode 100644 tests/ovn-sbctl.at
diff --git a/manpages.mk b/manpages.mk
index 3cec260..6e2853b 100644
--- a/manpages.mk
+++ b/manpages.mk
@@ -1,5 +1,17 @@
# Generated automatically -- do not modify! -*- buffer-read-only: t -*-
+ovn/utilities/ovn-sbctl.8: \
+ ovn/utilities/ovn-sbctl.8.in \
+ lib/db-ctl-base.man \
+ lib/table.man \
+ ovsdb/remote-active.man \
+ ovsdb/remote-passive.man
+ovn/utilities/ovn-sbctl.8.in:
+lib/db-ctl-base.man:
+lib/table.man:
+ovsdb/remote-active.man:
+ovsdb/remote-passive.man:
+
ovsdb/ovsdb-client.1: \
ovsdb/ovsdb-client.1.in \
lib/common-syn.man \
diff --git a/ovn/.gitignore b/ovn/.gitignore
index 4c13616..5b3bc55 100644
--- a/ovn/.gitignore
+++ b/ovn/.gitignore
@@ -5,5 +5,3 @@
/ovn-sb.5
/ovn-sb.gv
/ovn-sb.pic
-/ovn-nbctl
-/ovn-nbctl.8
diff --git a/ovn/automake.mk b/ovn/automake.mk
index 459ee36..7d84c52 100644
--- a/ovn/automake.mk
+++ b/ovn/automake.mk
@@ -66,19 +66,14 @@ ovn/ovn-nb.5: \
$(srcdir)/ovn/ovn-nb.xml > $@.tmp && \
mv $@.tmp $@
-man_MANS += ovn/ovn-architecture.7 ovn/ovn-nbctl.8
-EXTRA_DIST += ovn/ovn-architecture.7.xml ovn/ovn-nbctl.8.xml
-DISTCLEANFILES += ovn/ovn-nbctl.8 ovn/ovn-architecture.7
+man_MANS += ovn/ovn-architecture.7
+EXTRA_DIST += ovn/ovn-architecture.7.xml
+DISTCLEANFILES += ovn/ovn-architecture.7
EXTRA_DIST += \
ovn/TODO \
ovn/CONTAINERS.OpenStack.md
-# ovn-nbctl
-bin_PROGRAMS += ovn/ovn-nbctl
-ovn_ovn_nbctl_SOURCES = ovn/ovn-nbctl.c
-ovn_ovn_nbctl_LDADD = ovn/lib/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la
-
include ovn/controller/automake.mk
include ovn/lib/automake.mk
include ovn/northd/automake.mk
diff --git a/ovn/utilities/.gitignore b/ovn/utilities/.gitignore
index 1ddc63e..197077e 100644
--- a/ovn/utilities/.gitignore
+++ b/ovn/utilities/.gitignore
@@ -1 +1,6 @@
/ovn-ctl.8
+/ovn-nbctl
+/ovn-nbctl.8
+/ovn-sbctl
+/ovn-sbctl.8
+
diff --git a/ovn/utilities/automake.mk b/ovn/utilities/automake.mk
index 1373864..827f7d9 100644
--- a/ovn/utilities/automake.mk
+++ b/ovn/utilities/automake.mk
@@ -10,3 +10,21 @@ EXTRA_DIST += \
DISTCLEANFILES += \
ovn/utilities/ovn-ctl.8
+
+# ovn-nbctl
+bin_PROGRAMS += ovn/utilities/ovn-nbctl
+ovn_utilities_ovn_nbctl_SOURCES = ovn/utilities/ovn-nbctl.c
+ovn_utilities_ovn_nbctl_LDADD = ovn/lib/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la
+
+man_MANS += ovn/utilities/ovn-nbctl.8
+EXTRA_DIST += ovn/utilities/ovn-nbctl.8.xml
+DISTCLEANFILES += ovn/utilities/ovn-nbctl.8
+
+# ovn-sbctl
+bin_PROGRAMS += ovn/utilities/ovn-sbctl
+ovn_utilities_ovn_sbctl_SOURCES = ovn/utilities/ovn-sbctl.c
+ovn_utilities_ovn_sbctl_LDADD = ovn/lib/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la
+
+MAN_ROOTS += ovn/utilities/ovn-sbctl.8.in
+man_MANS += ovn/utilities/ovn-sbctl.8
+DISTCLEANFILES += ovn/utilities/ovn-sbctl.8
diff --git a/ovn/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml
similarity index 100%
rename from ovn/ovn-nbctl.8.xml
rename to ovn/utilities/ovn-nbctl.8.xml
diff --git a/ovn/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
similarity index 100%
rename from ovn/ovn-nbctl.c
rename to ovn/utilities/ovn-nbctl.c
diff --git a/ovn/utilities/ovn-sbctl.8.in b/ovn/utilities/ovn-sbctl.8.in
new file mode 100644
index 0000000..1cf9b7d
--- /dev/null
+++ b/ovn/utilities/ovn-sbctl.8.in
@@ -0,0 +1,160 @@
+.\" -*- nroff -*-
+.de IQ
+. br
+. ns
+. IP "\\$1"
+..
+.de ST
+. PP
+. RS -0.15in
+. I "\\$1"
+. RE
+..
+.TH ovn\-sbctl 8 "@VERSION@" "Open vSwitch" "Open vSwitch 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 command is mainly for examining the \fBOVN_Southbound\fR database
+for monitoring or troubleshooting. The modification commands provided
+(e.g. Chassis Commands) are mainly for testing.
+.PP
+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"
+Sets \fIserver\fR as the database server that \fBovn\-sbctl\fR
+contacts to query or modify configuration. The default is
+\fBunix:@RUNDIR@/db.sock\fR. \fIserver\fR must take one of the
+following forms:
+.RS
+.so ovsdb/remote-active.man
+.so ovsdb/remote-passive.man
+.RE
+.
+.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.)
+.
+.SS "Table Formatting Options"
+These options control the format of output from the \fBlist\fR and
+\fBfind\fR commands.
+.so lib/table.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 "\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] \fBadd\-chassis \fIchassis\fR \fIencap-type\fR \fIencap-ip\fR"
+Creates a new chassis named \fIchassis\fR. The chassis will have
+one encap entry with \fIencap-type\fR as tunnel type and \fIencap-ip\fR
+as destination ip.
+.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 as a real bridge.
+.
+.IP "[\fB\-\-if\-exists\fR] \fBdel\-chassis \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 "Binding Commands"
+.
+These commands manipulate \fBOVN_Southbound\fR bindings.
+.
+.IP "[\fB\-\-may\-exist\fR] \fBbind\-lport \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] \fBunbind\-lport\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.
+.
+.so lib/db-ctl-base.man
+.SH "EXIT STATUS"
+.IP "0"
+Successful program execution.
+.IP "1"
+Usage, syntax, or configuration file error.
+.IP "2"
+The \fIbridge\fR argument to \fBbr\-exists\fR specified the name of a
+bridge that does not exist.
+.SH "SEE ALSO"
+.
+.BR ovn\-sb (5).
diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
new file mode 100644
index 0000000..4a0d1b8
--- /dev/null
+++ b/ovn/utilities/ovn-sbctl.c
@@ -0,0 +1,835 @@
+/*
+ * Copyright (c) 2015 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <float.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "db-ctl-base.h"
+
+#include "command-line.h"
+#include "compiler.h"
+#include "dynamic-string.h"
+#include "fatal-signal.h"
+#include "json.h"
+#include "ovsdb-data.h"
+#include "ovsdb-idl.h"
+#include "poll-loop.h"
+#include "process.h"
+#include "sset.h"
+#include "shash.h"
+#include "ovn/lib/ovn-sb-idl.h"
+#include "table.h"
+#include "timeval.h"
+#include "util.h"
+#include "openvswitch/vlog.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 int timeout;
+
+/* Format for table output. */
+static struct table_style table_style = TABLE_STYLE_DEFAULT;
+
+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 void do_sbctl(const char *args, struct ctl_command *, size_t n,
+ struct ovsdb_idl *);
+
+int
+main(int argc, char *argv[])
+{
+ extern struct vlog_module VLM_reconnect;
+ struct ovsdb_idl *idl;
+ struct ctl_command *commands;
+ struct shash local_options;
+ unsigned int seqno;
+ size_t n_commands;
+ char *args;
+
+ set_program_name(argv[0]);
+ fatal_ignore_sigpipe();
+ vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
+ vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
+ sbrec_init();
+
+ sbctl_cmd_init();
+
+ /* Log our arguments. This is often valuable for debugging systems. */
+ args = process_escape_args(argv);
+ VLOG(ctl_might_write_to_db(argv) ? VLL_INFO : VLL_DBG, "Called as %s", args);
+
+ /* Parse command line. */
+ shash_init(&local_options);
+ parse_options(argc, argv, &local_options);
+ commands = ctl_parse_commands(argc - optind, argv + optind, &local_options,
+ &n_commands);
+
+ if (timeout) {
+ time_alarm(timeout);
+ }
+
+ /* Initialize IDL. */
+ idl = the_idl = ovsdb_idl_create(db, &sbrec_idl_class, false, false);
+ 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);
+ do_sbctl(args, commands, n_commands, idl);
+ }
+
+ if (seqno == ovsdb_idl_get_seqno(idl)) {
+ ovsdb_idl_wait(idl);
+ poll_block();
+ }
+ }
+}
+
+static void
+parse_options(int argc, char *argv[], struct shash *local_options)
+{
+ enum {
+ OPT_DB = UCHAR_MAX + 1,
+ OPT_ONELINE,
+ OPT_NO_SYSLOG,
+ OPT_DRY_RUN,
+ OPT_PEER_CA_CERT,
+ OPT_LOCAL,
+ OPT_COMMANDS,
+ OPT_OPTIONS,
+ VLOG_OPTION_ENUMS,
+ TABLE_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},
+ {"version", no_argument, NULL, 'V'},
+ VLOG_LONG_OPTIONS,
+ 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);
+ table_style.format = TF_LIST;
+
+ 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(&VLM_sbctl, 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),
+ optarg ? xstrdup(optarg) : NULL);
+ break;
+
+ case 'h':
+ usage();
+
+ case OPT_COMMANDS:
+ ctl_print_commands();
+
+ case OPT_OPTIONS:
+ ctl_print_options(global_long_options);
+
+ case 'V':
+ ovs_print_version(0, 0);
+ printf("DB Schema %s\n", sbrec_get_db_version());
+ exit(EXIT_SUCCESS);
+
+ case 't':
+ timeout = strtoul(optarg, NULL, 10);
+ if (timeout < 0) {
+ ctl_fatal("value %s on -t or --timeout is invalid",
+ optarg);
+ }
+ break;
+
+ VLOG_OPTION_HANDLERS
+ TABLE_OPTION_HANDLERS(&table_style)
+
+ case '?':
+ exit(EXIT_FAILURE);
+
+ default:
+ abort();
+ }
+ }
+ free(short_options);
+
+ if (!db) {
+ db = ctl_default_db();
+ }
+
+ for (i = n_global_long_options; options[i].name; i++) {
+ free(CONST_CAST(char *, options[i].name));
+ }
+ free(options);
+}
+
+static void
+usage(void)
+{
+ printf("\
+%s: ovs-vswitchd management utility\n\
+\n\
+for debugging and testing only, never use it in production\n\
+\n\
+usage: %s [OPTIONS] COMMAND [ARG...]\n\
+\n\
+SouthBound DB commands:\n\
+ show print overview of database contents\n\
+\n\
+Chassis commands:\n\
+ add-chassis CHASSIS create a new chassis named CHASSIS\n\
+ del-chassis CHASSIS delete CHASSIS and all of its encaps,\n\
+ and gateway_ports\n\
+\n\
+Binding commands:\n\
+ bind-lport LPORT CHASSIS bind logical port LPORT to CHASSIS\n\
+ unbind-lport LPORT delete the binding of logical port LPORT\n\
+\n\
+%s\
+\n\
+Options:\n\
+ --db=DATABASE connect to DATABASE\n\
+ (default: %s)\n\
+ -t, --timeout=SECS wait at most SECS seconds for ovs-vswitchd\n\
+ --dry-run do not commit changes to database\n\
+ --oneline print exactly one line of output per command\n",
+ program_name, program_name, ctl_get_db_cmd_usage(), ctl_default_db());
+ vlog_usage();
+ printf("\
+ --no-syslog equivalent to --verbose=sbctl:syslog:warn\n");
+ printf("\n\
+Other options:\n\
+ -h, --help display this help message\n\
+ -V, --version display version information\n");
+ exit(EXIT_SUCCESS);
+}
+
+
+/* ovs-sbctl specific context. Inherits the 'struct ctl_context' as base. */
+struct sbctl_context {
+ struct ctl_context base;
+
+ /* A cache of the contents of the database.
+ *
+ * A command that needs to use any of this information must first call
+ * sbctl_context_populate_cache(). A command that changes anything that
+ * could invalidate the cache must either call
+ * sbctl_context_invalidate_cache() or manually update the cache to
+ * maintain its correctness. */
+ bool cache_valid;
+ struct shash chassis; /* Maps from chassis name to struct sbctl_chassis. */
+ struct shash bindings; /* Maps from lport name to struct sbctl_binding. */
+};
+
+/* Casts 'base' into 'strcut sbctl_context'. */
+static struct sbctl_context *
+sbctl_context_cast(struct ctl_context *base)
+{
+ return CONTAINER_OF(base, struct sbctl_context, base);
+}
+
+struct sbctl_chassis {
+ const struct sbrec_chassis *ch_cfg;
+};
+
+struct sbctl_binding {
+ const struct sbrec_binding *bd_cfg;
+};
+
+static void
+sbctl_context_invalidate_cache(struct ctl_context *ctx)
+{
+ struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
+
+ if (!sbctl_ctx->cache_valid) {
+ return;
+ }
+ sbctl_ctx->cache_valid = false;
+ shash_destroy_free_data(&sbctl_ctx->chassis);
+ shash_destroy_free_data(&sbctl_ctx->bindings);
+}
+
+static void
+sbctl_context_populate_cache(struct ctl_context *ctx)
+{
+ struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
+ const struct sbrec_chassis *chassis_rec;
+ const struct sbrec_binding *binding_rec;
+ struct sset chassis, 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->bindings);
+ sset_init(&chassis);
+ SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->idl) {
+ struct sbctl_chassis *ch;
+
+ if (!sset_add(&chassis, chassis_rec->name)) {
+ VLOG_WARN("database contains duplicate chassis name (%s)",
+ chassis_rec->name);
+ continue;
+ }
+
+ ch = xmalloc(sizeof *ch);
+ ch->ch_cfg = chassis_rec;
+ shash_add(&sbctl_ctx->chassis, chassis_rec->name, ch);
+ }
+ sset_destroy(&chassis);
+
+ sset_init(&bindings);
+ SBREC_BINDING_FOR_EACH(binding_rec, ctx->idl) {
+ struct sbctl_binding *bd;
+
+ if (!sset_add(&bindings, binding_rec->logical_port)) {
+ VLOG_WARN("database contains duplicate binding for logical "
+ "port (%s)",
+ binding_rec->logical_port);
+ continue;
+ }
+
+ bd = xmalloc(sizeof *bd);
+ bd->bd_cfg = binding_rec;
+ shash_add(&sbctl_ctx->bindings, binding_rec->logical_port, bd);
+ }
+ sset_destroy(&bindings);
+}
+
+static void
+check_conflicts(struct sbctl_context *sbctl_ctx, const char *name,
+ char *msg)
+{
+ if (shash_find(&sbctl_ctx->chassis, name)) {
+ ctl_fatal("%s because a chassis named %s already exists",
+ msg, name);
+ }
+ free(msg);
+}
+
+static struct sbctl_chassis *
+find_chassis(struct sbctl_context *sbctl_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);
+ if (must_exist && !sbctl_ch) {
+ ctl_fatal("no chassis named %s", name);
+ }
+
+ return sbctl_ch;
+}
+
+static struct sbctl_binding *
+find_binding(struct sbctl_context *sbctl_ctx, const char *name, bool must_exist)
+{
+ struct sbctl_binding *bd;
+
+ ovs_assert(sbctl_ctx->cache_valid);
+
+ bd = shash_find_data(&sbctl_ctx->bindings, name);
+ if (must_exist && !bd) {
+ ctl_fatal("no port named %s", name);
+ }
+
+ return bd;
+}
+
+static void
+pre_get_info(struct ctl_context *ctx)
+{
+ ovsdb_idl_add_column(ctx->idl, &sbrec_chassis_col_name);
+ ovsdb_idl_add_column(ctx->idl, &sbrec_chassis_col_encaps);
+
+ ovsdb_idl_add_column(ctx->idl, &sbrec_encap_col_type);
+ ovsdb_idl_add_column(ctx->idl, &sbrec_encap_col_ip);
+
+ ovsdb_idl_add_column(ctx->idl, &sbrec_binding_col_logical_port);
+ ovsdb_idl_add_column(ctx->idl, &sbrec_binding_col_chassis);
+}
+
+struct cmd_show_table cmd_show_tables[] = {
+ {&sbrec_table_chassis,
+ &sbrec_chassis_col_name,
+ {&sbrec_chassis_col_encaps,
+ NULL,
+ NULL},
+ false},
+
+ {&sbrec_table_encap,
+ &sbrec_encap_col_type,
+ {&sbrec_encap_col_ip,
+ &sbrec_encap_col_options,
+ NULL},
+ false},
+
+ {NULL, NULL, {NULL, NULL, NULL}, false},
+};
+
+static void
+cmd_add_chassis(struct ctl_context *ctx)
+{
+ struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
+ struct sbrec_chassis *ch;
+ struct sbrec_encap *encap;
+ bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
+ const char *ch_name, *encap_type, *encap_ip;
+
+ ch_name = ctx->argv[1];
+ encap_type = ctx->argv[2];
+ encap_ip = ctx->argv[3];
+
+ sbctl_context_populate_cache(ctx);
+ if (may_exist) {
+ struct sbctl_chassis *sbctl_ch;
+
+ sbctl_ch = find_chassis(sbctl_ctx, ch_name, false);
+ if (sbctl_ch) {
+ return;
+ }
+ }
+ check_conflicts(sbctl_ctx, ch_name,
+ xasprintf("cannot create a chassis named %s", ch_name));
+ ch = sbrec_chassis_insert(ctx->txn);
+ sbrec_chassis_set_name(ch, ch_name);
+ encap = sbrec_encap_insert(ctx->txn);
+ sbrec_encap_set_type(encap, encap_type);
+ sbrec_encap_set_ip(encap, encap_ip);
+ sbrec_chassis_set_encaps(ch, &encap, 1);
+ sbctl_context_invalidate_cache(ctx);
+}
+
+static void
+cmd_del_chassis(struct ctl_context *ctx)
+{
+ struct sbctl_context *sbctl_ctx = sbctl_context_cast(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);
+ if (sbctl_ch) {
+ if (sbctl_ch->ch_cfg) {
+ sbrec_chassis_delete(sbctl_ch->ch_cfg);
+ }
+ shash_find_and_delete(&sbctl_ctx->chassis, ctx->argv[1]);
+ free(sbctl_ch);
+ }
+}
+
+static void
+cmd_bind_lport(struct ctl_context *ctx)
+{
+ struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
+ bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
+ struct sbctl_chassis *sbctl_ch;
+ struct sbctl_binding *sbctl_bd;
+ char *lport_name, *ch_name;
+
+ /* binding must exist, chassis must exist! */
+ lport_name = ctx->argv[1];
+ ch_name = ctx->argv[2];
+
+ sbctl_context_populate_cache(ctx);
+ sbctl_bd = find_binding(sbctl_ctx, lport_name, true);
+ sbctl_ch = find_chassis(sbctl_ctx, ch_name, true);
+
+ 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)",
+ lport_name, sbctl_bd->bd_cfg->chassis->name);
+ }
+ }
+ sbrec_binding_set_chassis(sbctl_bd->bd_cfg, sbctl_ch->ch_cfg);
+ sbctl_context_invalidate_cache(ctx);
+}
+
+static void
+cmd_unbind_lport(struct ctl_context *ctx)
+{
+ struct sbctl_context *sbctl_ctx = sbctl_context_cast(ctx);
+ bool must_exist = !shash_find(&ctx->options, "--if-exists");
+ struct sbctl_binding *sbctl_bd;
+ char *lport_name;
+
+ lport_name = ctx->argv[1];
+ sbctl_context_populate_cache(ctx);
+ sbctl_bd = find_binding(sbctl_ctx, lport_name, must_exist);
+ if (sbctl_bd) {
+ sbrec_binding_set_chassis(sbctl_bd->bd_cfg, NULL);
+ }
+}
+
+
+static const struct ctl_table_class tables[] = {
+ {&sbrec_table_chassis,
+ {{&sbrec_table_chassis, &sbrec_chassis_col_name, NULL},
+ {NULL, NULL, NULL}}},
+
+ {&sbrec_table_encap,
+ {{NULL, NULL, NULL},
+ {NULL, NULL, NULL}}},
+
+ {&sbrec_table_pipeline,
+ {{&sbrec_table_pipeline, NULL, &sbrec_pipeline_col_logical_datapath},
+ {NULL, NULL, NULL}}},
+
+ {&sbrec_table_binding,
+ {{&sbrec_table_binding, &sbrec_binding_col_logical_port, NULL},
+ {NULL, NULL, NULL}}},
+
+ {NULL, {{NULL, NULL, NULL}, {NULL, 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)
+{
+ struct ctl_command *c;
+
+ for (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);
+ sbctl_context_done(&sbctl_ctx, c);
+
+ ovs_assert(!c->output.string);
+ ovs_assert(!c->table);
+ }
+ }
+}
+
+static void
+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;
+ char *error = NULL;
+
+ 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);
+
+ 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);
+ }
+ 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);
+ sbctl_context_done(&sbctl_ctx, c);
+ }
+ }
+ }
+ error = xstrdup(ovsdb_idl_txn_get_error(txn));
+
+ 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", error);
+
+ case TXN_NOT_LOCKED:
+ /* Should not happen--we never call ovsdb_idl_set_lock(). */
+ ctl_fatal("database not locked");
+
+ default:
+ OVS_NOT_REACHED();
+ }
+ free(error);
+
+ 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);
+
+ exit(EXIT_SUCCESS);
+
+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. */
+ if (txn) {
+ 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);
+ }
+ free(error);
+}
+
+static const struct ctl_command_syntax sbctl_commands[] = {
+ /* Chassis commands. */
+ {"add-chassis", 3, 3, "CHASSIS ENCAP-TYPE ENCAP-IP", pre_get_info,
+ cmd_add_chassis, NULL, "--may-exist", RW},
+ {"del-chassis", 1, 1, "CHASSIS", pre_get_info, cmd_del_chassis, NULL,
+ "--if-exists", RW},
+
+ /* Binding commands. */
+ {"bind-lport", 2, 2, "LPORT CHASSIS", pre_get_info, cmd_bind_lport, NULL,
+ "--may-exist", RW},
+ {"unbind-lport", 1, 1, "LPORT", pre_get_info, cmd_unbind_lport, NULL,
+ "--if-exists", RW},
+
+ /* SSL commands (To Be Added). */
+
+ {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO},
+};
+
+/* Registers sbctl and common db commands. */
+static void
+sbctl_cmd_init(void)
+{
+ ctl_init(tables);
+ ctl_register_commands(sbctl_commands);
+}
diff --git a/tests/automake.mk b/tests/automake.mk
index 153d4e1..1f16f3c 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -83,7 +83,8 @@ TESTSUITE_AT = \
tests/vlog.at \
tests/vtep-ctl.at \
tests/auto-attach.at \
- tests/ovn.at
+ tests/ovn.at \
+ tests/ovn-sbctl.at
KMOD_TESTSUITE_AT = \
tests/kmod-testsuite.at \
@@ -95,7 +96,7 @@ TESTSUITE_PATCH = $(srcdir)/tests/testsuite.patch
KMOD_TESTSUITE = $(srcdir)/tests/kmod-testsuite
DISTCLEANFILES += tests/atconfig tests/atlocal
-AUTOTEST_PATH = utilities:vswitchd:ovsdb:vtep:tests:$(PTHREAD_WIN32_DIR_DLL)
+AUTOTEST_PATH = utilities:vswitchd:ovsdb:vtep:tests:$(PTHREAD_WIN32_DIR_DLL):ovn:ovn/northd:ovn/utilities
check-local: tests/atconfig tests/atlocal $(TESTSUITE)
$(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH=$(AUTOTEST_PATH) $(TESTSUITEFLAGS)
diff --git a/tests/ovn-sbctl.at b/tests/ovn-sbctl.at
new file mode 100644
index 0000000..4db1d1c
--- /dev/null
+++ b/tests/ovn-sbctl.at
@@ -0,0 +1,61 @@
+AT_BANNER([ovn_controller_gw])
+
+# OVN_SBCTL_TEST_START
+m4_define([OVN_SBCTL_TEST_START],
+ [OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+ OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+ OVS_DBDIR=`pwd`; export OVS_DBDIR
+ OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR
+
+ 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%%-*}/${daemon}.ovsschema])
+ done
+
+ dnl Start ovsdb-server.
+ AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:$OVS_RUNDIR/db.sock ovn-nb.db ovn-sb.db], [0], [], [stderr])
+ ON_EXIT_UNQUOTED([kill `cat ovsdb-server.pid`])
+ 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 --pidfile --log-file --ovnnb-db=unix:$OVS_RUNDIR/db.sock --ovnsb-db=unix:$OVS_RUNDIR/db.sock], [0], [], [stderr])
+ ON_EXIT_UNQUOTED([kill `cat ovn-northd.pid`])
+ AT_CHECK([[sed < stderr '
+/vlog|INFO|opened log file/d']])
+ AT_CAPTURE_FILE([ovn-northd.log])
+])
+
+# OVN_SBCTL_TEST_STOP
+m4_define([OVN_SBCTL_TEST_STOP],
+ [AT_CHECK([check_logs $1])
+ AT_CHECK([ovs-appctl -t ovn-northd exit])
+ AT_CHECK([ovs-appctl -t ovsdb-server exit])])
+
+# ovn-sbctl test.
+AT_SETUP([ovn-sbctl - test])
+OVN_SBCTL_TEST_START
+
+AT_CHECK([ovn-nbctl lswitch-add br-test])
+AT_CHECK([ovn-nbctl lport-add br-test vif0])
+AT_CHECK([ovn-nbctl lport-set-macs vif0 f0:ab:cd:ef:01:02])
+AT_CHECK([ovn-sbctl add-chassis ch0 stt 1.2.3.5])
+AT_CHECK([ovn-sbctl bind-lport vif0 ch0])
+
+AT_CHECK([ovn-sbctl show], [0], [dnl
+Chassis "ch0"
+ Encap stt
+ ip: "1.2.3.5"
+])
+
+uuid=$(ovn-sbctl --columns=_uuid list Chassis ch0 | cut -d ':' -f2 | tr -d ' ')
+AT_CHECK_UNQUOTED([ovn-sbctl --columns=logical_port,mac,chassis list Binding], [0], [dnl
+logical_port : "vif0"
+mac : [["f0:ab:cd:ef:01:02"]]
+chassis : ${uuid}
+])
+
+OVN_SBCTL_TEST_STOP
+AT_CLEANUP
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 92b788b..f706f67 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -68,3 +68,4 @@ m4_include([tests/vlog.at])
m4_include([tests/vtep-ctl.at])
m4_include([tests/auto-attach.at])
m4_include([tests/ovn.at])
+m4_include([tests/ovn-sbctl.at])
--
1.7.9.5
More information about the dev
mailing list