vtep-ctl: Use db-ctl-base.
authorAlex Wang <alexw@nicira.com>
Thu, 11 Jun 2015 21:09:27 +0000 (14:09 -0700)
committerAlex Wang <alexw@nicira.com>
Tue, 23 Jun 2015 16:43:24 +0000 (09:43 -0700)
This commit makes vtep-ctl use db-ctl-base to avoid duplicate code.
As an addition, a 'show' command is added to vtep-ctl.

Signed-off-by: Alex Wang <alexw@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
manpages.mk
tests/vtep-ctl.at
vtep/vtep-ctl.8.in
vtep/vtep-ctl.c

index 4cc3bf2..3cec260 100644 (file)
@@ -279,6 +279,7 @@ ovsdb/remote-passive.man:
 vtep/vtep-ctl.8: \
        vtep/vtep-ctl.8.in \
        lib/common.man \
+       lib/db-ctl-base.man \
        lib/ssl-bootstrap.man \
        lib/ssl-peer-ca-cert.man \
        lib/ssl.man \
@@ -290,6 +291,7 @@ vtep/vtep-ctl.8: \
        ovsdb/remote-passive.man
 vtep/vtep-ctl.8.in:
 lib/common.man:
+lib/db-ctl-base.man:
 lib/ssl-bootstrap.man:
 lib/ssl-peer-ca-cert.man:
 lib/ssl.man:
index 40ee16e..67007bf 100644 (file)
@@ -887,3 +887,25 @@ tcp:5.4.3.2\ntcp:8.9.10.11
 ], [], [VTEP_CTL_CLEANUP])
 VTEP_CTL_CLEANUP
 AT_CLEANUP
+
+AT_SETUP([show command])
+AT_KEYWORDS([vtep-ctl show])
+VTEP_CTL_SETUP
+AT_CHECK([RUN_VTEP_CTL(
+  [set-manager tcp:4.5.6.7],
+  [add-ps a],
+  [add-port a a1],
+  [add-ls ls1],
+  [bind-ls a a1 100 ls1],
+  [set Physical_Switch a tunnel_ips=[[1.2.3.4]]])], [0], [ignore], [], [VTEP_CTL_CLEANUP])
+
+AT_CHECK([vtep-ctl --timeout=5 -vreconnect:emer --db=unix:socket show | tail -n+2 | sed 's/=[[a-f0-9-]][[a-f0-9-]]*\}/=<ls>\}/' ], [0], [dnl
+    Manager "tcp:4.5.6.7"
+    Physical_Switch a
+        tunnel_ips: [["1.2.3.4"]]
+        Physical_Port "a1"
+            vlan_bindings: {100=<ls>}
+], [], [VTEP_CTL_CLEANUP])
+
+VTEP_CTL_CLEANUP
+AT_CLEANUP
index 3203854..21fb6db 100644 (file)
@@ -348,232 +348,7 @@ and \fB\-\-\fR and \fB_\fR are treated interchangeably.  Unique
 abbreviations are acceptable, e.g. \fBman\fR or \fBm\fR is sufficient
 to identify the \fBManager\fR table.
 .
-.ST "Database Values"
-.PP
-Each column in the database accepts a fixed type of data.  The
-currently defined basic types, and their representations, are:
-.IP "integer"
-A decimal integer in the range \-2**63 to 2**63\-1, inclusive.
-.IP "real"
-A floating-point number.
-.IP "Boolean"
-True or false, written \fBtrue\fR or \fBfalse\fR, respectively.
-.IP "string"
-An arbitrary Unicode string, except that null bytes are not allowed.
-Quotes are optional for most strings that begin with an English letter
-or underscore and consist only of letters, underscores, hyphens, and
-periods.  However, \fBtrue\fR and \fBfalse\fR and strings that match
-the syntax of UUIDs (see below) must be enclosed in double quotes to
-distinguish them from other basic types.  When double quotes are used,
-the syntax is that of strings in JSON, e.g. backslashes may be used to
-escape special characters.  The empty string must be represented as a
-pair of double quotes (\fB""\fR).
-.IP "UUID"
-Either a universally unique identifier in the style of RFC 4122,
-e.g. \fBf81d4fae\-7dec\-11d0\-a765\-00a0c91e6bf6\fR, or an \fB@\fIname\fR
-defined by a \fBget\fR or \fBcreate\fR command within the same \fBvtep\-ctl\fR
-invocation.
-.PP
-Multiple values in a single column may be separated by spaces or a
-single comma.  When multiple values are present, duplicates are not
-allowed, and order is not important.  Conversely, some database
-columns can have an empty set of values, represented as \fB[]\fR, and
-square brackets may optionally enclose other non-empty sets or single
-values as well.
-.PP
-A few database columns are ``maps'' of key-value pairs, where the key
-and the value are each some fixed database type.  These are specified
-in the form \fIkey\fB=\fIvalue\fR, where \fIkey\fR and \fIvalue\fR
-follow the syntax for the column's key type and value type,
-respectively.  When multiple pairs are present (separated by spaces or
-a comma), duplicate keys are not allowed, and again the order is not
-important.  Duplicate values are allowed.  An empty map is represented
-as \fB{}\fR.  Curly braces may optionally enclose non-empty maps as
-well (but use quotes to prevent the shell from expanding
-\fBother-config={0=x,1=y}\fR into \fBother-config=0=x
-other-config=1=y\fR, which may not have the desired effect).
-.
-.ST "Database Command Syntax"
-.IP "[\fB\-\-columns=\fIcolumn\fR[\fB,\fIcolumn\fR]...] \fBlist \fItable \fR[\fIrecord\fR]..."
-Lists the data in each specified \fIrecord\fR.  If no
-records are specified, lists all the records in \fItable\fR.
-.IP
-If \fB\-\-columns\fR is specified, only the requested columns are
-listed, in the specified order.  Otherwise, all columns are listed, in
-alphabetical order by column name.
-.
-.IP "[\fB\-\-columns=\fIcolumn\fR[\fB,\fIcolumn\fR]...] \fBfind \fItable \fR[\fIcolumn\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR]..."
-Lists the data in each record in \fItable\fR whose \fIcolumn\fR equals
-\fIvalue\fR or, if \fIkey\fR is specified, whose \fIcolumn\fR contains
-a \fIkey\fR with the specified \fIvalue\fR.  The following operators
-may be used where \fB=\fR is written in the syntax summary:
-.RS
-.IP "\fB= != < > <= >=\fR"
-Selects records in which \fIcolumn\fR[\fB:\fIkey\fR] equals, does not
-equal, is less than, is greater than, is less than or equal to, or is
-greater than or equal to \fIvalue\fR, respectively.
-.IP
-Consider \fIcolumn\fR[\fB:\fIkey\fR] and \fIvalue\fR as sets of
-elements.  Identical sets are considered equal.  Otherwise, if the
-sets have different numbers of elements, then the set with more
-elements is considered to be larger.  Otherwise, consider a element
-from each set pairwise, in increasing order within each set.  The
-first pair that differs determines the result.  (For a column that
-contains key-value pairs, first all the keys are compared, and values
-are considered only if the two sets contain identical keys.)
-.IP "\fB{=} {!=}\fR"
-Test for set equality or inequality, respectively.
-.IP "\fB{<=}\fR"
-Selects records in which \fIcolumn\fR[\fB:\fIkey\fR] is a subset of
-\fIvalue\fR.  For example, \fBflood-vlans{<=}1,2\fR selects records in
-which the \fBflood-vlans\fR column is the empty set or contains 1 or 2
-or both.
-.IP "\fB{<}\fR"
-Selects records in which \fIcolumn\fR[\fB:\fIkey\fR] is a proper
-subset of \fIvalue\fR.  For example, \fBflood-vlans{<}1,2\fR selects
-records in which the \fBflood-vlans\fR column is the empty set or
-contains 1 or 2 but not both.
-.IP "\fB{>=} {>}\fR"
-Same as \fB{<=}\fR and \fB{<}\fR, respectively, except that the
-relationship is reversed.  For example, \fBflood-vlans{>=}1,2\fR
-selects records in which the \fBflood-vlans\fR column contains both 1
-and 2.
-.RE
-.IP
-For arithmetic operators (\fB= != < > <= >=\fR), when \fIkey\fR is
-specified but a particular record's \fIcolumn\fR does not contain
-\fIkey\fR, the record is always omitted from the results.  Thus, the
-condition \fBother-config:mtu!=1500\fR matches records that have a
-\fBmtu\fR key whose value is not 1500, but not those that lack an
-\fBmtu\fR key.
-.IP
-For the set operators, when \fIkey\fR is specified but a particular
-record's \fIcolumn\fR does not contain \fIkey\fR, the comparison is
-done against an empty set.  Thus, the condition
-\fBother-config:mtu{!=}1500\fR matches records that have a \fBmtu\fR
-key whose value is not 1500 and those that lack an \fBmtu\fR key.
-.IP
-Don't forget to escape \fB<\fR or \fB>\fR from interpretation by the
-shell.
-.IP
-If \fB\-\-columns\fR is specified, only the requested columns are
-listed, in the specified order.  Otherwise all columns are listed, in
-alphabetical order by column name.
-.IP
-The UUIDs shown for rows created in the same \fBvtep\-ctl\fR
-invocation will be wrong.
-.
-.IP "[\fB\-\-id=@\fIname\fR] [\fB\-\-if\-exists\fR] \fBget \fItable record \fR[\fIcolumn\fR[\fB:\fIkey\fR]]..."
-Prints the value of each specified \fIcolumn\fR in the given
-\fIrecord\fR in \fItable\fR.  For map columns, a \fIkey\fR may
-optionally be specified, in which case the value associated with
-\fIkey\fR in the column is printed, instead of the entire map.
-.IP
-For a map column, without \fB\-\-if\-exists\fR it is an error if
-\fIkey\fR does not exist; with it, a blank line is printed.  If
-\fIcolumn\fR is not a map column or if \fIkey\fR is not specified,
-\fB\-\-if\-exists\fR has no effect.
-.IP
-If \fB@\fIname\fR is specified, then the UUID for \fIrecord\fR may be
-referred to by that name later in the same \fBvtep\-ctl\fR
-invocation in contexts where a UUID is expected.
-.IP
-Both \fB\-\-id\fR and the \fIcolumn\fR arguments are optional, but
-usually at least one or the other should be specified.  If both are
-omitted, then \fBget\fR has no effect except to verify that
-\fIrecord\fR exists in \fItable\fR.
-.
-.IP "\fBset \fItable record column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..."
-Sets the value of each specified \fIcolumn\fR in the given
-\fIrecord\fR in \fItable\fR to \fIvalue\fR.  For map columns, a
-\fIkey\fR may optionally be specified, in which case the value
-associated with \fIkey\fR in that column is changed (or added, if none
-exists), instead of the entire map.
-.
-.IP "\fBadd \fItable record column \fR[\fIkey\fB=\fR]\fIvalue\fR..."
-Adds the specified value or key-value pair to \fIcolumn\fR in
-\fIrecord\fR in \fItable\fR.  If \fIcolumn\fR is a map, then \fIkey\fR
-is required, otherwise it is prohibited.  If \fIkey\fR already exists
-in a map column, then the current \fIvalue\fR is not replaced (use the
-\fBset\fR command to replace an existing value).
-.
-.IP "\fBremove \fItable record column \fR\fIvalue\fR..."
-.IQ "\fBremove \fItable record column \fR\fIkey\fR..."
-.IQ "\fBremove \fItable record column \fR\fIkey\fB=\fR\fIvalue\fR..."
-Removes the specified values or key-value pairs from \fIcolumn\fR in
-\fIrecord\fR in \fItable\fR.  The first form applies to columns that
-are not maps: each specified \fIvalue\fR is removed from the column.
-The second and third forms apply to map columns: if only a \fIkey\fR
-is specified, then any key-value pair with the given \fIkey\fR is
-removed, regardless of its value; if a \fIvalue\fR is given then a
-pair is removed only if both key and value match.
-.IP
-It is not an error if the column does not contain the specified key or
-value or pair.
-.
-.IP "\fBclear\fR \fItable record column\fR..."
-Sets each \fIcolumn\fR in \fIrecord\fR in \fItable\fR to the empty set
-or empty map, as appropriate.  This command applies only to columns
-that are allowed to be empty.
-.
-.IP "[\fB\-\-id=@\fIname\fR] \fBcreate\fR \fItable column\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR..."
-Creates a new record in \fItable\fR and sets the initial values of
-each \fIcolumn\fR.  Columns not explicitly set will receive their
-default values.  Outputs the UUID of the new row.
-.IP
-If \fB@\fIname\fR is specified, then the UUID for the new row may be
-referred to by that name elsewhere in the same \fBvtep\-ctl\fR
-invocation in contexts where a UUID is expected.  Such references may
-precede or follow the \fBcreate\fR command.
-.IP
-Records in the Open vSwitch database are significant only when they
-can be reached directly or indirectly from the \fBOpen_vSwitch\fR
-table.  Except for records in the \fBQoS\fR or \fBQueue\fR tables,
-records that are not reachable from the \fBOpen_vSwitch\fR table are
-automatically deleted from the database.  This deletion happens
-immediately, without waiting for additional \fBvtep\-ctl\fR commands
-or other database activity.  Thus, a \fBcreate\fR command must
-generally be accompanied by additional commands \fIwithin the same
-\fBvtep\-ctl\fI invocation\fR to add a chain of references to the
-newly created record from the top-level \fBOpen_vSwitch\fR record.
-The \fBEXAMPLES\fR section gives some examples that show how to do
-this.
-.
-.IP "\fR[\fB\-\-if\-exists\fR] \fBdestroy \fItable record\fR..."
-Deletes each specified \fIrecord\fR from \fItable\fR.  Unless
-\fB\-\-if\-exists\fR is specified, each \fIrecord\fRs must exist.
-.IP "\fB\-\-all destroy \fItable\fR"
-Deletes all records from the \fItable\fR.
-.IP
-The \fBdestroy\fR command is only useful for records in the \fBQoS\fR
-or \fBQueue\fR tables.  Records in other tables are automatically
-deleted from the database when they become unreachable from the
-\fBOpen_vSwitch\fR table.  This means that deleting the last reference
-to a record is sufficient for deleting the record itself.  For records
-in these tables, \fBdestroy\fR is silently ignored.  See the
-\fBEXAMPLES\fR section below for more information.
-.
-.IP "\fBwait\-until \fItable record \fR[\fIcolumn\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR]..."
-Waits until \fItable\fR contains a record named \fIrecord\fR whose
-\fIcolumn\fR equals \fIvalue\fR or, if \fIkey\fR is specified, whose
-\fIcolumn\fR contains a \fIkey\fR with the specified \fIvalue\fR.  Any
-of the operators \fB!=\fR, \fB<\fR, \fB>\fR, \fB<=\fR, or \fB>=\fR may
-be substituted for \fB=\fR to test for inequality, less than, greater
-than, less than or equal to, or greater than or equal to,
-respectively.  (Don't forget to escape \fB<\fR or \fB>\fR from
-interpretation by the shell.)
-.IP
-If no \fIcolumn\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR arguments are given,
-this command waits only until \fIrecord\fR exists.  If more than one
-such argument is given, the command waits until all of them are
-satisfied.
-.IP
-Consider specifying \fB\-\-timeout=0\fR along with
-\fB\-\-wait\-until\fR, to prevent \fBvtep\-ctl\fR from terminating
-after waiting only at most 5 seconds.
-.IP "\fBcomment \fR[\fIarg\fR]..."
-This command has no effect on behavior, but any database log record
-created by the command will include the command and its arguments.
+.so lib/db-ctl-base.man
 .PP
 .SH "EXIT STATUS"
 .IP "0"
index a501a98..309c0b3 100644 (file)
 #include <string.h>
 #include <unistd.h>
 
+#include "db-ctl-base.h"
+
 #include "command-line.h"
 #include "compiler.h"
-#include "dirs.h"
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "hash.h"
 
 VLOG_DEFINE_THIS_MODULE(vtep_ctl);
 
-/* vtep_ctl_fatal() also logs the error, so it is preferred in this file. */
-#define ovs_fatal please_use_vtep_ctl_fatal_instead_of_ovs_fatal
-
 struct vtep_ctl_context;
 
-/* A command supported by vtep-ctl. */
-struct vtep_ctl_command_syntax {
-    const char *name;           /* e.g. "add-ps" */
-    int min_args;               /* Min number of arguments following name. */
-    int max_args;               /* Max number of arguments following name. */
-
-    /* If nonnull, calls ovsdb_idl_add_column() or ovsdb_idl_add_table() for
-     * each column or table in ctx->idl that it uses. */
-    void (*prerequisites)(struct vtep_ctl_context *ctx);
-
-    /* Does the actual work of the command and puts the command's output, if
-     * any, in ctx->output or ctx->table.
-     *
-     * Alternatively, if some prerequisite of the command is not met and the
-     * caller should wait for something to change and then retry, it may set
-     * ctx->try_again to true.  (Only the "wait-until" command currently does
-     * this.) */
-    void (*run)(struct vtep_ctl_context *ctx);
-
-    /* If nonnull, called after the transaction has been successfully
-     * committed.  ctx->output is the output from the "run" function, which
-     * this function may modify and otherwise postprocess as needed.  (Only the
-     * "create" command currently does any postprocessing.) */
-    void (*postprocess)(struct vtep_ctl_context *ctx);
-
-    /* A comma-separated list of supported options, e.g. "--a,--b", or the
-     * empty string if the command does not support any options. */
-    const char *options;
-    enum { RO, RW } mode;       /* Does this command modify the database? */
-};
-
-struct vtep_ctl_command {
-    /* Data that remains constant after initialization. */
-    const struct vtep_ctl_command_syntax *syntax;
-    int argc;
-    char **argv;
-    struct shash options;
-
-    /* Data modified by commands. */
-    struct ds output;
-    struct table *table;
-};
-
 /* --db: The database server to contact. */
 static const char *db;
 
@@ -115,51 +70,23 @@ static 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 vtep_ctl_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 vtep_ctl_exit(int status);
-OVS_NO_RETURN static void vtep_ctl_fatal(const char *, ...) OVS_PRINTF_FORMAT(1, 2);
-static char *default_db(void);
+static void vtep_ctl_cmd_init(void);
 OVS_NO_RETURN static void usage(void);
 static void parse_options(int argc, char *argv[], struct shash *local_options);
-static bool might_write_to_db(char **argv);
-
-static struct vtep_ctl_command *parse_commands(int argc, char *argv[],
-                                            struct shash *local_options,
-                                            size_t *n_commandsp);
-static void parse_command(int argc, char *argv[], struct shash *local_options,
-                          struct vtep_ctl_command *);
-static const struct vtep_ctl_command_syntax *find_command(const char *name);
-static void run_prerequisites(struct vtep_ctl_command[], size_t n_commands,
+static void run_prerequisites(struct ctl_command[], size_t n_commands,
                               struct ovsdb_idl *);
-static void do_vtep_ctl(const char *args, struct vtep_ctl_command *, size_t n,
-                     struct ovsdb_idl *);
-
-static const struct vtep_ctl_table_class *get_table(const char *table_name);
-static void set_column(const struct vtep_ctl_table_class *,
-                       const struct ovsdb_idl_row *, const char *arg,
-                       struct ovsdb_symbol_table *);
-
-static bool is_condition_satisfied(const struct vtep_ctl_table_class *,
-                                   const struct ovsdb_idl_row *,
-                                   const char *arg,
-                                   struct ovsdb_symbol_table *);
-
+static void do_vtep_ctl(const char *args, struct ctl_command *, size_t n,
+                        struct ovsdb_idl *);
 static struct vtep_ctl_lswitch *find_lswitch(struct vtep_ctl_context *,
                                              const char *name,
                                              bool must_exist);
-static const struct vtep_ctl_command_syntax *get_all_commands(void);
 
 int
 main(int argc, char *argv[])
 {
     extern struct vlog_module VLM_reconnect;
     struct ovsdb_idl *idl;
-    struct vtep_ctl_command *commands;
+    struct ctl_command *commands;
     struct shash local_options;
     unsigned int seqno;
     size_t n_commands;
@@ -171,15 +98,17 @@ main(int argc, char *argv[])
     vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
     vteprec_init();
 
+    vtep_ctl_cmd_init();
+
     /* Log our arguments.  This is often valuable for debugging systems. */
     args = process_escape_args(argv);
-    VLOG(might_write_to_db(argv) ? VLL_INFO : VLL_DBG, "Called as %s", args);
+    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 = parse_commands(argc - optind, argv + optind, &local_options,
-                              &n_commands);
+    commands = ctl_parse_commands(argc - optind, argv + optind, &local_options,
+                                  &n_commands);
 
     if (timeout) {
         time_alarm(timeout);
@@ -212,30 +141,6 @@ main(int argc, char *argv[])
     }
 }
 
-static struct option *
-find_option(const char *name, struct option *options, size_t n_options)
-{
-    size_t i;
-
-    for (i = 0; i < n_options; i++) {
-        if (!strcmp(options[i].name, name)) {
-            return &options[i];
-        }
-    }
-    return NULL;
-}
-
-static struct option *
-add_option(struct option **optionsp, size_t *n_optionsp,
-           size_t *allocated_optionsp)
-{
-    if (*n_optionsp >= *allocated_optionsp) {
-        *optionsp = x2nrealloc(*optionsp, allocated_optionsp,
-                               sizeof **optionsp);
-    }
-    return &(*optionsp)[(*n_optionsp)++];
-}
-
 static void
 parse_options(int argc, char *argv[], struct shash *local_options)
 {
@@ -266,8 +171,7 @@ parse_options(int argc, char *argv[], struct shash *local_options)
     const int n_global_long_options = ARRAY_SIZE(global_long_options) - 1;
     char *tmp, *short_options;
 
-    const struct vtep_ctl_command_syntax *p;
-    struct option *options, *o;
+    struct option *options;
     size_t allocated_options;
     size_t n_options;
     size_t i;
@@ -283,48 +187,7 @@ parse_options(int argc, char *argv[], struct shash *local_options)
     options = xmemdup(global_long_options, sizeof global_long_options);
     allocated_options = ARRAY_SIZE(global_long_options);
     n_options = n_global_long_options;
-    for (p = get_all_commands(); p->name; p++) {
-        if (p->options[0]) {
-            char *save_ptr = NULL;
-            char *name;
-            char *s;
-
-            s = xstrdup(p->options);
-            for (name = strtok_r(s, ",", &save_ptr); name != NULL;
-                 name = strtok_r(NULL, ",", &save_ptr)) {
-                char *equals;
-                int has_arg;
-
-                ovs_assert(name[0] == '-' && name[1] == '-' && name[2]);
-                name += 2;
-
-                equals = strchr(name, '=');
-                if (equals) {
-                    has_arg = required_argument;
-                    *equals = '\0';
-                } else {
-                    has_arg = no_argument;
-                }
-
-                o = find_option(name, options, n_options);
-                if (o) {
-                    ovs_assert(o - options >= n_global_long_options);
-                    ovs_assert(o->has_arg == has_arg);
-                } else {
-                    o = add_option(&options, &n_options, &allocated_options);
-                    o->name = xstrdup(name);
-                    o->has_arg = has_arg;
-                    o->flag = NULL;
-                    o->val = OPT_LOCAL;
-                }
-            }
-
-            free(s);
-        }
-    }
-    o = add_option(&options, &n_options, &allocated_options);
-    memset(o, 0, sizeof *o);
-
+    ctl_add_cmd_options(&options, &n_options, &allocated_options, OPT_LOCAL);
     table_style.format = TF_LIST;
 
     for (;;) {
@@ -355,8 +218,8 @@ parse_options(int argc, char *argv[], struct shash *local_options)
 
         case OPT_LOCAL:
             if (shash_find(local_options, options[idx].name)) {
-                vtep_ctl_fatal("'%s' option specified multiple times",
-                            options[idx].name);
+                ctl_fatal("'%s' option specified multiple times",
+                          options[idx].name);
             }
             shash_add_nocopy(local_options,
                              xasprintf("--%s", options[idx].name),
@@ -374,8 +237,8 @@ parse_options(int argc, char *argv[], struct shash *local_options)
         case 't':
             timeout = strtoul(optarg, NULL, 10);
             if (timeout < 0) {
-                vtep_ctl_fatal("value %s on -t or --timeout is invalid",
-                            optarg);
+                ctl_fatal("value %s on -t or --timeout is invalid",
+                          optarg);
             }
             break;
 
@@ -398,7 +261,7 @@ parse_options(int argc, char *argv[], struct shash *local_options)
     free(short_options);
 
     if (!db) {
-        db = default_db();
+        db = ctl_default_db();
     }
 
     for (i = n_global_long_options; options[i].name; i++) {
@@ -407,182 +270,6 @@ parse_options(int argc, char *argv[], struct shash *local_options)
     free(options);
 }
 
-static struct vtep_ctl_command *
-parse_commands(int argc, char *argv[], struct shash *local_options,
-               size_t *n_commandsp)
-{
-    struct vtep_ctl_command *commands;
-    size_t n_commands, allocated_commands;
-    int i, start;
-
-    commands = NULL;
-    n_commands = allocated_commands = 0;
-
-    for (start = i = 0; i <= argc; i++) {
-        if (i == argc || !strcmp(argv[i], "--")) {
-            if (i > start) {
-                if (n_commands >= allocated_commands) {
-                    struct vtep_ctl_command *c;
-
-                    commands = x2nrealloc(commands, &allocated_commands,
-                                          sizeof *commands);
-                    for (c = commands; c < &commands[n_commands]; c++) {
-                        shash_moved(&c->options);
-                    }
-                }
-                parse_command(i - start, &argv[start], local_options,
-                              &commands[n_commands++]);
-            } else if (!shash_is_empty(local_options)) {
-                vtep_ctl_fatal("missing command name (use --help for help)");
-            }
-            start = i + 1;
-        }
-    }
-    if (!n_commands) {
-        vtep_ctl_fatal("missing command name (use --help for help)");
-    }
-    *n_commandsp = n_commands;
-    return commands;
-}
-
-static void
-parse_command(int argc, char *argv[], struct shash *local_options,
-              struct vtep_ctl_command *command)
-{
-    const struct vtep_ctl_command_syntax *p;
-    struct shash_node *node;
-    int n_arg;
-    int i;
-
-    shash_init(&command->options);
-    shash_swap(local_options, &command->options);
-    for (i = 0; i < argc; i++) {
-        const char *option = argv[i];
-        const char *equals;
-        char *key, *value;
-
-        if (option[0] != '-') {
-            break;
-        }
-
-        equals = strchr(option, '=');
-        if (equals) {
-            key = xmemdup0(option, equals - option);
-            value = xstrdup(equals + 1);
-        } else {
-            key = xstrdup(option);
-            value = NULL;
-        }
-
-        if (shash_find(&command->options, key)) {
-            vtep_ctl_fatal("'%s' option specified multiple times", argv[i]);
-        }
-        shash_add_nocopy(&command->options, key, value);
-    }
-    if (i == argc) {
-        vtep_ctl_fatal("missing command name (use --help for help)");
-    }
-
-    p = find_command(argv[i]);
-    if (!p) {
-        vtep_ctl_fatal("unknown command '%s'; use --help for help", argv[i]);
-    }
-
-    SHASH_FOR_EACH (node, &command->options) {
-        const char *s = strstr(p->options, node->name);
-        int end = s ? s[strlen(node->name)] : EOF;
-
-        if (end != '=' && end != ',' && end != ' ' && end != '\0') {
-            vtep_ctl_fatal("'%s' command has no '%s' option",
-                        argv[i], node->name);
-        }
-        if ((end == '=') != (node->data != NULL)) {
-            if (end == '=') {
-                vtep_ctl_fatal("missing argument to '%s' option on '%s' "
-                            "command", node->name, argv[i]);
-            } else {
-                vtep_ctl_fatal("'%s' option on '%s' does not accept an "
-                            "argument", node->name, argv[i]);
-            }
-        }
-    }
-
-    n_arg = argc - i - 1;
-    if (n_arg < p->min_args) {
-        vtep_ctl_fatal("'%s' command requires at least %d arguments",
-                    p->name, p->min_args);
-    } else if (n_arg > p->max_args) {
-        int j;
-
-        for (j = i + 1; j < argc; j++) {
-            if (argv[j][0] == '-') {
-                vtep_ctl_fatal("'%s' command takes at most %d arguments "
-                            "(note that options must precede command "
-                            "names and follow a \"--\" argument)",
-                            p->name, p->max_args);
-            }
-        }
-
-        vtep_ctl_fatal("'%s' command takes at most %d arguments",
-                    p->name, p->max_args);
-    }
-
-    command->syntax = p;
-    command->argc = n_arg + 1;
-    command->argv = &argv[i];
-}
-
-/* Returns the "struct vtep_ctl_command_syntax" for a given command 'name', or a
- * null pointer if there is none. */
-static const struct vtep_ctl_command_syntax *
-find_command(const char *name)
-{
-    static struct shash commands = SHASH_INITIALIZER(&commands);
-
-    if (shash_is_empty(&commands)) {
-        const struct vtep_ctl_command_syntax *p;
-
-        for (p = get_all_commands(); p->name; p++) {
-            shash_add_assert(&commands, p->name, p);
-        }
-    }
-
-    return shash_find_data(&commands, name);
-}
-
-static void
-vtep_ctl_fatal(const char *format, ...)
-{
-    char *message;
-    va_list args;
-
-    va_start(args, format);
-    message = xvasprintf(format, args);
-    va_end(args);
-
-    vlog_set_levels(&VLM_vtep_ctl, VLF_CONSOLE, VLL_OFF);
-    VLOG_ERR("%s", message);
-    ovs_error(0, "%s", message);
-    vtep_ctl_exit(EXIT_FAILURE);
-}
-
-/* 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
-vtep_ctl_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 void
 usage(void)
 {
@@ -590,6 +277,9 @@ usage(void)
 %s: VTEP configuration utility\n\
 usage: %s [OPTIONS] COMMAND [ARG...]\n\
 \n\
+VTEP commands:\n\
+  show                        print overview of database contents\n\
+\n\
 Manager commands:\n\
   get-manager                 print the managers\n\
   del-manager                 delete the managers\n\
@@ -629,18 +319,7 @@ MAC binding commands:\n\
   clear-remote-macs LS                clear remote mac entries\n\
   list-remote-macs LS                 list remote mac entries\n\
 \n\
-Database commands:\n\
-  list TBL [REC]              list RECord (or all records) in TBL\n\
-  find TBL CONDITION...       list records satisfying CONDITION in TBL\n\
-  get TBL REC COL[:KEY]       print values of COLumns in RECord in TBL\n\
-  set TBL REC COL[:KEY]=VALUE set COLumn values in RECord in TBL\n\
-  add TBL REC COL [KEY=]VALUE add (KEY=)VALUE to COLumn in RECord in TBL\n\
-  remove TBL REC COL [KEY=]VALUE  remove (KEY=)VALUE from COLumn\n\
-  clear TBL REC COL           clear values from COLumn in RECord in TBL\n\
-  create TBL COL[:KEY]=VALUE  create and initialize new record\n\
-  destroy TBL REC             delete RECord from TBL\n\
-  wait-until TBL REC [COL[:KEY]=VALUE]  wait until condition is true\n\
-Potentially unsafe database commands require --force option.\n\
+%s\
 \n\
 Options:\n\
   --db=DATABASE               connect to DATABASE\n\
@@ -648,7 +327,7 @@ Options:\n\
   -t, --timeout=SECS          wait at most SECS seconds\n\
   --dry-run                   do not commit changes to database\n\
   --oneline                   print exactly one line of output per command\n",
-           program_name, program_name, default_db());
+           program_name, program_name, ctl_get_db_cmd_usage(), ctl_default_db());
     vlog_usage();
     printf("\
   --no-syslog                 equivalent to --verbose=vtep_ctl:syslog:warn\n");
@@ -660,43 +339,44 @@ Other options:\n\
     exit(EXIT_SUCCESS);
 }
 
-static char *
-default_db(void)
-{
-    static char *def;
-    if (!def) {
-        def = xasprintf("unix:%s/db.sock", ovs_rundir());
-    }
-    return def;
-}
-
-/* Returns true if it looks like this set of arguments might modify the
- * database, otherwise false.  (Not very smart, so it's prone to false
- * positives.) */
-static bool
-might_write_to_db(char **argv)
-{
-    for (; *argv; argv++) {
-        const struct vtep_ctl_command_syntax *p = find_command(*argv);
-        if (p && p->mode == RW) {
-            return true;
-        }
-    }
-    return false;
-}
 \f
+struct cmd_show_table cmd_show_tables[] = {
+    {&vteprec_table_global,
+     NULL,
+     {&vteprec_global_col_managers,
+      &vteprec_global_col_switches,
+      NULL},
+     false},
+
+    {&vteprec_table_manager,
+     &vteprec_manager_col_target,
+     {&vteprec_manager_col_is_connected,
+      NULL,
+      NULL},
+     false},
+
+    {&vteprec_table_physical_switch,
+     &vteprec_physical_switch_col_name,
+     {&vteprec_physical_switch_col_tunnel_ips,
+      &vteprec_physical_switch_col_ports,
+      NULL},
+     false},
+
+    {&vteprec_table_physical_port,
+     &vteprec_physical_port_col_name,
+     {&vteprec_physical_port_col_vlan_bindings,
+      NULL,
+      NULL},
+     false},
+
+    {NULL, NULL, {NULL, NULL, NULL}, false}
+};
+
+/* vtep-ctl specific context.  Inherits the 'struct ctl_context' as base. */
 struct vtep_ctl_context {
-    /* Read-only. */
-    int argc;
-    char **argv;
-    struct shash options;
+    struct ctl_context base;
 
     /* Modifiable state. */
-    struct ds output;
-    struct table *table;
-    struct ovsdb_idl *idl;
-    struct ovsdb_idl_txn *txn;
-    struct ovsdb_symbol_table *symtab;
     const struct vteprec_global *vtep_global;
     bool verified_ports;
 
@@ -716,12 +396,15 @@ struct vtep_ctl_context {
                              * struct vtep_ctl_lswitch. */
     struct shash plocs;     /* Maps from "<encap>+<dst_ip>" to
                              * struct vteprec_physical_locator. */
-
-    /* A command may set this member to true if some prerequisite is not met
-     * and the caller should wait for something to change and then retry. */
-    bool try_again;
 };
 
+/* Casts 'base' into 'strcut vtep_ctl_context'. */
+static struct vtep_ctl_context *
+vtep_ctl_context_cast(struct ctl_context *base)
+{
+    return CONTAINER_OF(base, struct vtep_ctl_context, base);
+}
+
 struct vtep_ctl_pswitch {
     const struct vteprec_physical_switch *ps_cfg;
     char *name;
@@ -759,22 +442,22 @@ struct vtep_ctl_ploc {
 };
 
 static void
-verify_ports(struct vtep_ctl_context *ctx)
+verify_ports(struct vtep_ctl_context *vtepctl_ctx)
 {
-    if (!ctx->verified_ports) {
+    if (!vtepctl_ctx->verified_ports) {
         const struct vteprec_physical_switch *ps;
 
-        vteprec_global_verify_switches(ctx->vtep_global);
-        VTEPREC_PHYSICAL_SWITCH_FOR_EACH (ps, ctx->idl) {
+        vteprec_global_verify_switches(vtepctl_ctx->vtep_global);
+        VTEPREC_PHYSICAL_SWITCH_FOR_EACH (ps, vtepctl_ctx->base.idl) {
             vteprec_physical_switch_verify_ports(ps);
         }
 
-        ctx->verified_ports = true;
+        vtepctl_ctx->verified_ports = true;
     }
 }
 
 static struct vtep_ctl_port *
-add_port_to_cache(struct vtep_ctl_context *ctx,
+add_port_to_cache(struct vtep_ctl_context *vtepctl_ctx,
                   struct vtep_ctl_pswitch *ps,
                   struct vteprec_physical_port *port_cfg)
 {
@@ -785,7 +468,7 @@ add_port_to_cache(struct vtep_ctl_context *ctx,
     list_push_back(&ps->ports, &port->ports_node);
     port->port_cfg = port_cfg;
     port->ps = ps;
-    shash_add(&ctx->ports, cache_name, port);
+    shash_add(&vtepctl_ctx->ports, cache_name, port);
     free(cache_name);
     shash_init(&port->bindings);
 
@@ -793,26 +476,27 @@ add_port_to_cache(struct vtep_ctl_context *ctx,
 }
 
 static void
-del_cached_port(struct vtep_ctl_context *ctx, struct vtep_ctl_port *port)
+del_cached_port(struct vtep_ctl_context *vtepctl_ctx,
+                struct vtep_ctl_port *port)
 {
     char *cache_name = xasprintf("%s+%s", port->ps->name, port->port_cfg->name);
 
     list_remove(&port->ports_node);
-    shash_find_and_delete(&ctx->ports, cache_name);
+    shash_find_and_delete(&vtepctl_ctx->ports, cache_name);
     vteprec_physical_port_delete(port->port_cfg);
     free(cache_name);
     free(port);
 }
 
 static struct vtep_ctl_pswitch *
-add_pswitch_to_cache(struct vtep_ctl_context *ctx,
+add_pswitch_to_cache(struct vtep_ctl_context *vtepctl_ctx,
                      struct vteprec_physical_switch *ps_cfg)
 {
     struct vtep_ctl_pswitch *ps = xmalloc(sizeof *ps);
     ps->ps_cfg = ps_cfg;
     ps->name = xstrdup(ps_cfg->name);
     list_init(&ps->ports);
-    shash_add(&ctx->pswitches, ps->name, ps);
+    shash_add(&vtepctl_ctx->pswitches, ps->name, ps);
     return ps;
 }
 
@@ -848,13 +532,13 @@ del_cached_pswitch(struct vtep_ctl_context *ctx, struct vtep_ctl_pswitch *ps)
 }
 
 static struct vtep_ctl_lswitch *
-add_lswitch_to_cache(struct vtep_ctl_context *ctx,
+add_lswitch_to_cache(struct vtep_ctl_context *vtepctl_ctx,
                      const struct vteprec_logical_switch *ls_cfg)
 {
     struct vtep_ctl_lswitch *ls = xmalloc(sizeof *ls);
     ls->ls_cfg = ls_cfg;
     ls->name = xstrdup(ls_cfg->name);
-    shash_add(&ctx->lswitches, ls->name, ls);
+    shash_add(&vtepctl_ctx->lswitches, ls->name, ls);
     shash_init(&ls->ucast_local);
     shash_init(&ls->ucast_remote);
     shash_init(&ls->mcast_local);
@@ -908,7 +592,7 @@ add_ls_binding_to_cache(struct vtep_ctl_port *port,
                         struct vtep_ctl_lswitch *ls)
 {
     if (shash_find(&port->bindings, vlan)) {
-        vtep_ctl_fatal("multiple bindings for vlan %s", vlan);
+        ctl_fatal("multiple bindings for vlan %s", vlan);
     }
 
     shash_add(&port->bindings, vlan, ls);
@@ -918,37 +602,37 @@ static void
 del_cached_ls_binding(struct vtep_ctl_port *port, const char *vlan)
 {
     if (!shash_find(&port->bindings, vlan)) {
-        vtep_ctl_fatal("no binding for vlan %s", vlan);
+        ctl_fatal("no binding for vlan %s", vlan);
     }
 
     shash_find_and_delete(&port->bindings, vlan);
 }
 
 static struct vteprec_physical_locator *
-find_ploc(struct vtep_ctl_context *ctx, const char *encap,
+find_ploc(struct vtep_ctl_context *vtepctl_ctx, const char *encap,
           const char *dst_ip)
 {
     struct vteprec_physical_locator *ploc;
     char *name = xasprintf("%s+%s", encap, dst_ip);
 
-    ovs_assert(ctx->cache_valid);
+    ovs_assert(vtepctl_ctx->cache_valid);
 
-    ploc = shash_find_data(&ctx->plocs, name);
+    ploc = shash_find_data(&vtepctl_ctx->plocs, name);
     free(name);
 
     return ploc;
 }
 
 static void
-add_ploc_to_cache(struct vtep_ctl_context *ctx,
+add_ploc_to_cache(struct vtep_ctl_context *vtepctl_ctx,
                   struct vteprec_physical_locator *ploc)
 {
     char *name = xasprintf("%s+%s", ploc->encapsulation_type, ploc->dst_ip);
     struct vteprec_physical_locator *orig_ploc;
 
-    orig_ploc = find_ploc(ctx, ploc->encapsulation_type, ploc->dst_ip);
+    orig_ploc = find_ploc(vtepctl_ctx, ploc->encapsulation_type, ploc->dst_ip);
     if (!orig_ploc) {
-        shash_add(&ctx->plocs, name, ploc);
+        shash_add(&vtepctl_ctx->plocs, name, ploc);
     }
 
     free(name);
@@ -981,7 +665,7 @@ del_ploc_from_mcast_mac(struct vtep_ctl_mcast_mac *mcast_mac,
 }
 
 static struct vtep_ctl_mcast_mac *
-add_mcast_mac_to_cache(struct vtep_ctl_context *ctx,
+add_mcast_mac_to_cache(struct vtep_ctl_context *vtepctl_ctx,
                        struct vtep_ctl_lswitch *ls, const char *mac,
                        struct vteprec_physical_locator_set *ploc_set_cfg,
                        bool local)
@@ -996,42 +680,43 @@ add_mcast_mac_to_cache(struct vtep_ctl_context *ctx,
     mcast_mac->ploc_set_cfg = ploc_set_cfg;
     list_init(&mcast_mac->locators);
     shash_add(mcast_shash, mac, mcast_mac);
+
     for (i = 0; i < ploc_set_cfg->n_locators; i++) {
         struct vteprec_physical_locator *ploc_cfg;
 
         ploc_cfg = ploc_set_cfg->locators[i];
         add_ploc_to_mcast_mac(mcast_mac, ploc_cfg);
-        add_ploc_to_cache(ctx, ploc_cfg);
+        add_ploc_to_cache(vtepctl_ctx, ploc_cfg);
     }
 
     return mcast_mac;
 }
 
 static void
-vtep_ctl_context_invalidate_cache(struct vtep_ctl_context *ctx)
+vtep_ctl_context_invalidate_cache(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct shash_node *node;
 
-    if (!ctx->cache_valid) {
+    if (!vtepctl_ctx->cache_valid) {
         return;
     }
-    ctx->cache_valid = false;
+    vtepctl_ctx->cache_valid = false;
 
-    SHASH_FOR_EACH (node, &ctx->pswitches) {
+    SHASH_FOR_EACH (node, &vtepctl_ctx->pswitches) {
         struct vtep_ctl_pswitch *ps = node->data;
         free(ps->name);
         free(ps);
     }
-    shash_destroy(&ctx->pswitches);
+    shash_destroy(&vtepctl_ctx->pswitches);
 
-    SHASH_FOR_EACH (node, &ctx->ports) {
+    SHASH_FOR_EACH (node, &vtepctl_ctx->ports) {
         struct vtep_ctl_port *port = node->data;
         shash_destroy(&port->bindings);
     }
-    shash_destroy_free_data(&ctx->ports);
+    shash_destroy_free_data(&vtepctl_ctx->ports);
 
-    SHASH_FOR_EACH (node, &ctx->lswitches) {
+    SHASH_FOR_EACH (node, &vtepctl_ctx->lswitches) {
         struct vtep_ctl_lswitch *ls = node->data;
         struct shash_node *node2, *next_node2;
 
@@ -1065,12 +750,12 @@ vtep_ctl_context_invalidate_cache(struct vtep_ctl_context *ctx)
         free(ls->name);
         free(ls);
     }
-    shash_destroy(&ctx->lswitches);
-    shash_destroy(&ctx->plocs);
+    shash_destroy(&vtepctl_ctx->lswitches);
+    shash_destroy(&vtepctl_ctx->plocs);
 }
 
 static void
-pre_get_info(struct vtep_ctl_context *ctx)
+pre_get_info(struct ctl_context *ctx)
 {
     ovsdb_idl_add_column(ctx->idl, &vteprec_global_col_switches);
 
@@ -1118,9 +803,10 @@ pre_get_info(struct vtep_ctl_context *ctx)
 }
 
 static void
-vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
+vtep_ctl_context_populate_cache(struct ctl_context *ctx)
 {
-    const struct vteprec_global *vtep_global = ctx->vtep_global;
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
+    const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global;
     const struct vteprec_logical_switch *ls_cfg;
     const struct vteprec_ucast_macs_local *ucast_local_cfg;
     const struct vteprec_ucast_macs_remote *ucast_remote_cfg;
@@ -1130,15 +816,15 @@ vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
     struct sset pswitches, ports, lswitches;
     size_t i;
 
-    if (ctx->cache_valid) {
+    if (vtepctl_ctx->cache_valid) {
         /* Cache is already populated. */
         return;
     }
-    ctx->cache_valid = true;
-    shash_init(&ctx->pswitches);
-    shash_init(&ctx->ports);
-    shash_init(&ctx->lswitches);
-    shash_init(&ctx->plocs);
+    vtepctl_ctx->cache_valid = true;
+    shash_init(&vtepctl_ctx->pswitches);
+    shash_init(&vtepctl_ctx->ports);
+    shash_init(&vtepctl_ctx->lswitches);
+    shash_init(&vtepctl_ctx->plocs);
 
     sset_init(&pswitches);
     sset_init(&ports);
@@ -1152,7 +838,7 @@ vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
                       ps_cfg->name);
             continue;
         }
-        ps = add_pswitch_to_cache(ctx, ps_cfg);
+        ps = add_pswitch_to_cache(vtepctl_ctx, ps_cfg);
         if (!ps) {
             continue;
         }
@@ -1176,7 +862,7 @@ vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
                       ls_cfg->name);
             continue;
         }
-        add_lswitch_to_cache(ctx, ls_cfg);
+        add_lswitch_to_cache(vtepctl_ctx, ls_cfg);
     }
     sset_destroy(&lswitches);
 
@@ -1186,13 +872,14 @@ vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
         if (!ucast_local_cfg->logical_switch) {
             continue;
         }
-        ls = find_lswitch(ctx, ucast_local_cfg->logical_switch->name, false);
+        ls = find_lswitch(vtepctl_ctx, ucast_local_cfg->logical_switch->name,
+                          false);
         if (!ls) {
             continue;
         }
 
         if (ucast_local_cfg->locator) {
-            add_ploc_to_cache(ctx, ucast_local_cfg->locator);
+            add_ploc_to_cache(vtepctl_ctx, ucast_local_cfg->locator);
         }
 
         shash_add(&ls->ucast_local, ucast_local_cfg->MAC, ucast_local_cfg);
@@ -1204,13 +891,14 @@ vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
         if (!ucast_remote_cfg->logical_switch) {
             continue;
         }
-        ls = find_lswitch(ctx, ucast_remote_cfg->logical_switch->name, false);
+        ls = find_lswitch(vtepctl_ctx, ucast_remote_cfg->logical_switch->name,
+                          false);
         if (!ls) {
             continue;
         }
 
         if (ucast_remote_cfg->locator) {
-            add_ploc_to_cache(ctx, ucast_remote_cfg->locator);
+            add_ploc_to_cache(vtepctl_ctx, ucast_remote_cfg->locator);
         }
 
         shash_add(&ls->ucast_remote, ucast_remote_cfg->MAC, ucast_remote_cfg);
@@ -1223,12 +911,13 @@ vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
         if (!mcast_local_cfg->logical_switch) {
             continue;
         }
-        ls = find_lswitch(ctx, mcast_local_cfg->logical_switch->name, false);
+        ls = find_lswitch(vtepctl_ctx, mcast_local_cfg->logical_switch->name,
+                          false);
         if (!ls) {
             continue;
         }
 
-        mcast_mac = add_mcast_mac_to_cache(ctx, ls, mcast_local_cfg->MAC,
+        mcast_mac = add_mcast_mac_to_cache(vtepctl_ctx, ls, mcast_local_cfg->MAC,
                                            mcast_local_cfg->locator_set,
                                            true);
         mcast_mac->local_cfg = mcast_local_cfg;
@@ -1241,12 +930,13 @@ vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
         if (!mcast_remote_cfg->logical_switch) {
             continue;
         }
-        ls = find_lswitch(ctx, mcast_remote_cfg->logical_switch->name, false);
+        ls = find_lswitch(vtepctl_ctx, mcast_remote_cfg->logical_switch->name,
+                          false);
         if (!ls) {
             continue;
         }
 
-        mcast_mac = add_mcast_mac_to_cache(ctx, ls, mcast_remote_cfg->MAC,
+        mcast_mac = add_mcast_mac_to_cache(vtepctl_ctx, ls, mcast_remote_cfg->MAC,
                                            mcast_remote_cfg->locator_set,
                                            false);
         mcast_mac->remote_cfg = mcast_remote_cfg;
@@ -1254,10 +944,10 @@ vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
 
     VTEPREC_TUNNEL_FOR_EACH (tunnel_cfg, ctx->idl) {
         if (tunnel_cfg->local) {
-            add_ploc_to_cache(ctx, tunnel_cfg->local);
+            add_ploc_to_cache(vtepctl_ctx, tunnel_cfg->local);
         }
         if (tunnel_cfg->remote) {
-            add_ploc_to_cache(ctx, tunnel_cfg->remote);
+            add_ploc_to_cache(vtepctl_ctx, tunnel_cfg->remote);
         }
     }
 
@@ -1270,13 +960,13 @@ vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
         if (!sset_add(&pswitches, ps_cfg->name)) {
             continue;
         }
-        ps = shash_find_data(&ctx->pswitches, ps_cfg->name);
+        ps = shash_find_data(&vtepctl_ctx->pswitches, ps_cfg->name);
         for (j = 0; j < ps_cfg->n_ports; j++) {
             struct vteprec_physical_port *port_cfg = ps_cfg->ports[j];
             struct vtep_ctl_port *port;
             size_t k;
 
-            port = shash_find_data(&ctx->ports, port_cfg->name);
+            port = shash_find_data(&vtepctl_ctx->ports, port_cfg->name);
             if (port) {
                 if (port_cfg == port->port_cfg) {
                     VLOG_WARN("%s: port is in multiple physical switches "
@@ -1292,7 +982,7 @@ vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
                 continue;
             }
 
-            port = add_port_to_cache(ctx, ps, port_cfg);
+            port = add_port_to_cache(vtepctl_ctx, ps, port_cfg);
 
             for (k = 0; k < port_cfg->n_vlan_bindings; k++) {
                 struct vteprec_logical_switch *ls_cfg;
@@ -1301,11 +991,11 @@ vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
 
                 vlan = xasprintf("%"PRId64, port_cfg->key_vlan_bindings[k]);
                 if (shash_find(&port->bindings, vlan)) {
-                    vtep_ctl_fatal("multiple bindings for vlan %s", vlan);
+                    ctl_fatal("multiple bindings for vlan %s", vlan);
                 }
+
                 ls_cfg = port_cfg->value_vlan_bindings[k];
-                ls = find_lswitch(ctx, ls_cfg->name, true);
+                ls = find_lswitch(vtepctl_ctx, ls_cfg->name, true);
 
                 shash_add_nocopy(&port->bindings, vlan, ls);
             }
@@ -1315,38 +1005,38 @@ vtep_ctl_context_populate_cache(struct vtep_ctl_context *ctx)
 }
 
 static struct vtep_ctl_pswitch *
-find_pswitch(struct vtep_ctl_context *ctx, const char *name, bool must_exist)
+find_pswitch(struct vtep_ctl_context *vtepctl_ctx, const char *name, bool must_exist)
 {
     struct vtep_ctl_pswitch *ps;
 
-    ovs_assert(ctx->cache_valid);
+    ovs_assert(vtepctl_ctx->cache_valid);
 
-    ps = shash_find_data(&ctx->pswitches, name);
+    ps = shash_find_data(&vtepctl_ctx->pswitches, name);
     if (must_exist && !ps) {
-        vtep_ctl_fatal("no physical switch named %s", name);
+        ctl_fatal("no physical switch named %s", name);
     }
-    vteprec_global_verify_switches(ctx->vtep_global);
+    vteprec_global_verify_switches(vtepctl_ctx->vtep_global);
     return ps;
 }
 
 static struct vtep_ctl_port *
-find_port(struct vtep_ctl_context *ctx, const char *ps_name,
+find_port(struct vtep_ctl_context *vtepctl_ctx, const char *ps_name,
           const char *port_name, bool must_exist)
 {
     char *cache_name = xasprintf("%s+%s", ps_name, port_name);
     struct vtep_ctl_port *port;
 
-    ovs_assert(ctx->cache_valid);
+    ovs_assert(vtepctl_ctx->cache_valid);
 
-    port = shash_find_data(&ctx->ports, cache_name);
+    port = shash_find_data(&vtepctl_ctx->ports, cache_name);
     if (port && !strcmp(port_name, port->ps->name)) {
         port = NULL;
     }
     free(cache_name);
     if (must_exist && !port) {
-        vtep_ctl_fatal("no port named %s", port_name);
+        ctl_fatal("no port named %s", port_name);
     }
-    verify_ports(ctx);
+    verify_ports(vtepctl_ctx);
     return port;
 }
 
@@ -1402,17 +1092,18 @@ vtep_insert_pswitch(const struct vteprec_global *vtep_global,
 }
 
 static void
-cmd_add_ps(struct vtep_ctl_context *ctx)
+cmd_add_ps(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     const char *ps_name = ctx->argv[1];
     bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
     struct vteprec_physical_switch *ps;
 
     vtep_ctl_context_populate_cache(ctx);
-    if (find_pswitch(ctx, ps_name, false)) {
+    if (find_pswitch(vtepctl_ctx, ps_name, false)) {
         if (!may_exist) {
-            vtep_ctl_fatal("cannot create physical switch %s because it "
-                           "already exists", ps_name);
+            ctl_fatal("cannot create physical switch %s because it "
+                      "already exists", ps_name);
         }
         return;
     }
@@ -1420,40 +1111,41 @@ cmd_add_ps(struct vtep_ctl_context *ctx)
     ps = vteprec_physical_switch_insert(ctx->txn);
     vteprec_physical_switch_set_name(ps, ps_name);
 
-    vtep_insert_pswitch(ctx->vtep_global, ps);
+    vtep_insert_pswitch(vtepctl_ctx->vtep_global, ps);
 
     vtep_ctl_context_invalidate_cache(ctx);
 }
 
 static void
-del_port(struct vtep_ctl_context *ctx, struct vtep_ctl_port *port)
+del_port(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_port *port)
 {
     pswitch_delete_port(port->ps->ps_cfg, port->port_cfg);
-    del_cached_port(ctx, port);
+    del_cached_port(vtepctl_ctx, port);
 }
 
 static void
-del_pswitch(struct vtep_ctl_context *ctx, struct vtep_ctl_pswitch *ps)
+del_pswitch(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_pswitch *ps)
 {
     struct vtep_ctl_port *port, *next_port;
 
     LIST_FOR_EACH_SAFE (port, next_port, ports_node, &ps->ports) {
-        del_port(ctx, port);
+        del_port(vtepctl_ctx, port);
     }
 
-    del_cached_pswitch(ctx, ps);
+    del_cached_pswitch(vtepctl_ctx, ps);
 }
 
 static void
-cmd_del_ps(struct vtep_ctl_context *ctx)
+cmd_del_ps(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     bool must_exist = !shash_find(&ctx->options, "--if-exists");
     struct vtep_ctl_pswitch *ps;
 
     vtep_ctl_context_populate_cache(ctx);
-    ps = find_pswitch(ctx, ctx->argv[1], must_exist);
+    ps = find_pswitch(vtepctl_ctx, ctx->argv[1], must_exist);
     if (ps) {
-        del_pswitch(ctx, ps);
+        del_pswitch(vtepctl_ctx, ps);
     }
 }
 
@@ -1470,15 +1162,16 @@ output_sorted(struct svec *svec, struct ds *output)
 }
 
 static void
-cmd_list_ps(struct vtep_ctl_context *ctx)
+cmd_list_ps(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct shash_node *node;
     struct svec pswitches;
 
     vtep_ctl_context_populate_cache(ctx);
 
     svec_init(&pswitches);
-    SHASH_FOR_EACH (node, &ctx->pswitches) {
+    SHASH_FOR_EACH (node, &vtepctl_ctx->pswitches) {
         struct vtep_ctl_pswitch *ps = node->data;
 
         svec_add(&pswitches, ps->name);
@@ -1488,23 +1181,26 @@ cmd_list_ps(struct vtep_ctl_context *ctx)
 }
 
 static void
-cmd_ps_exists(struct vtep_ctl_context *ctx)
+cmd_ps_exists(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
+
     vtep_ctl_context_populate_cache(ctx);
-    if (!find_pswitch(ctx, ctx->argv[1], false)) {
-        vtep_ctl_exit(2);
+    if (!find_pswitch(vtepctl_ctx, ctx->argv[1], false)) {
+        ctl_exit(2);
     }
 }
 
 static void
-cmd_list_ports(struct vtep_ctl_context *ctx)
+cmd_list_ports(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct vtep_ctl_pswitch *ps;
     struct vtep_ctl_port *port;
     struct svec ports;
 
     vtep_ctl_context_populate_cache(ctx);
-    ps = find_pswitch(ctx, ctx->argv[1], true);
+    ps = find_pswitch(vtepctl_ctx, ctx->argv[1], true);
     vteprec_physical_switch_verify_ports(ps->ps_cfg);
 
     svec_init(&ports);
@@ -1518,92 +1214,97 @@ cmd_list_ports(struct vtep_ctl_context *ctx)
 }
 
 static void
-add_port(struct vtep_ctl_context *ctx, const char *ps_name,
+add_port(struct ctl_context *ctx, const char *ps_name,
          const char *port_name, bool may_exist)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct vtep_ctl_port *vtep_ctl_port;
     struct vtep_ctl_pswitch *ps;
     struct vteprec_physical_port *port;
 
     vtep_ctl_context_populate_cache(ctx);
 
-    vtep_ctl_port = find_port(ctx, ps_name, port_name, false);
+    vtep_ctl_port = find_port(vtepctl_ctx, ps_name, port_name, false);
     if (vtep_ctl_port) {
         if (!may_exist) {
-            vtep_ctl_fatal("cannot create a port named %s on %s because a "
-                           "port with that name already exists",
-                           port_name, ps_name);
+            ctl_fatal("cannot create a port named %s on %s because a "
+                      "port with that name already exists",
+                      port_name, ps_name);
         }
         return;
     }
 
-    ps = find_pswitch(ctx, ps_name, true);
+    ps = find_pswitch(vtepctl_ctx, ps_name, true);
 
     port = vteprec_physical_port_insert(ctx->txn);
     vteprec_physical_port_set_name(port, port_name);
 
     pswitch_insert_port(ps->ps_cfg, port);
 
-    add_port_to_cache(ctx, ps, port);
+    add_port_to_cache(vtepctl_ctx, ps, port);
 }
 
 static void
-cmd_add_port(struct vtep_ctl_context *ctx)
+cmd_add_port(struct ctl_context *ctx)
 {
     bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
+
     add_port(ctx, ctx->argv[1], ctx->argv[2], may_exist);
 }
 
 static void
-cmd_del_port(struct vtep_ctl_context *ctx)
+cmd_del_port(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     bool must_exist = !shash_find(&ctx->options, "--if-exists");
     struct vtep_ctl_port *port;
 
     vtep_ctl_context_populate_cache(ctx);
 
-    port = find_port(ctx, ctx->argv[1], ctx->argv[2], must_exist);
+    port = find_port(vtepctl_ctx, ctx->argv[1], ctx->argv[2], must_exist);
     if (port) {
         if (ctx->argc == 3) {
             struct vtep_ctl_pswitch *ps;
 
-            ps = find_pswitch(ctx, ctx->argv[1], true);
+            ps = find_pswitch(vtepctl_ctx, ctx->argv[1], true);
             if (port->ps != ps) {
-                vtep_ctl_fatal("physical switch %s does not have a port %s",
-                               ctx->argv[1], ctx->argv[2]);
+                ctl_fatal("physical switch %s does not have a port %s",
+                          ctx->argv[1], ctx->argv[2]);
             }
         }
 
-        del_port(ctx, port);
+        del_port(vtepctl_ctx, port);
     }
 }
 
 static struct vtep_ctl_lswitch *
-find_lswitch(struct vtep_ctl_context *ctx, const char *name, bool must_exist)
+find_lswitch(struct vtep_ctl_context *vtepctl_ctx,
+             const char *name, bool must_exist)
 {
     struct vtep_ctl_lswitch *ls;
 
-    ovs_assert(ctx->cache_valid);
+    ovs_assert(vtepctl_ctx->cache_valid);
 
-    ls = shash_find_data(&ctx->lswitches, name);
+    ls = shash_find_data(&vtepctl_ctx->lswitches, name);
     if (must_exist && !ls) {
-        vtep_ctl_fatal("no logical switch named %s", name);
+        ctl_fatal("no logical switch named %s", name);
     }
     return ls;
 }
 
 static void
-cmd_add_ls(struct vtep_ctl_context *ctx)
+cmd_add_ls(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     const char *ls_name = ctx->argv[1];
     bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
     struct vteprec_logical_switch *ls;
 
     vtep_ctl_context_populate_cache(ctx);
-    if (find_lswitch(ctx, ls_name, false)) {
+    if (find_lswitch(vtepctl_ctx, ls_name, false)) {
         if (!may_exist) {
-            vtep_ctl_fatal("cannot create logical switch %s because it "
-                           "already exists", ls_name);
+            ctl_fatal("cannot create logical switch %s because it "
+                      "already exists", ls_name);
         }
         return;
     }
@@ -1615,34 +1316,36 @@ cmd_add_ls(struct vtep_ctl_context *ctx)
 }
 
 static void
-del_lswitch(struct vtep_ctl_context *ctx, struct vtep_ctl_lswitch *ls)
+del_lswitch(struct vtep_ctl_context *vtepctl_ctx, struct vtep_ctl_lswitch *ls)
 {
-    del_cached_lswitch(ctx, ls);
+    del_cached_lswitch(vtepctl_ctx, ls);
 }
 
 static void
-cmd_del_ls(struct vtep_ctl_context *ctx)
+cmd_del_ls(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     bool must_exist = !shash_find(&ctx->options, "--if-exists");
     struct vtep_ctl_lswitch *ls;
 
     vtep_ctl_context_populate_cache(ctx);
-    ls = find_lswitch(ctx, ctx->argv[1], must_exist);
+    ls = find_lswitch(vtepctl_ctx, ctx->argv[1], must_exist);
     if (ls) {
-        del_lswitch(ctx, ls);
+        del_lswitch(vtepctl_ctx, ls);
     }
 }
 
 static void
-cmd_list_ls(struct vtep_ctl_context *ctx)
+cmd_list_ls(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct shash_node *node;
     struct svec lswitches;
 
     vtep_ctl_context_populate_cache(ctx);
 
     svec_init(&lswitches);
-    SHASH_FOR_EACH (node, &ctx->lswitches) {
+    SHASH_FOR_EACH (node, &vtepctl_ctx->lswitches) {
         struct vtep_ctl_lswitch *ls = node->data;
 
         svec_add(&lswitches, ls->name);
@@ -1652,29 +1355,32 @@ cmd_list_ls(struct vtep_ctl_context *ctx)
 }
 
 static void
-cmd_ls_exists(struct vtep_ctl_context *ctx)
+cmd_ls_exists(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
+
     vtep_ctl_context_populate_cache(ctx);
-    if (!find_lswitch(ctx, ctx->argv[1], false)) {
-        vtep_ctl_exit(2);
+    if (!find_lswitch(vtepctl_ctx, ctx->argv[1], false)) {
+        ctl_exit(2);
     }
 }
 
 static void
-cmd_list_bindings(struct vtep_ctl_context *ctx)
+cmd_list_bindings(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     const struct shash_node *node;
     struct vtep_ctl_port *port;
     struct svec bindings;
 
     vtep_ctl_context_populate_cache(ctx);
-    port = find_port(ctx, ctx->argv[1], ctx->argv[2], true);
+    port = find_port(vtepctl_ctx, ctx->argv[1], ctx->argv[2], true);
 
     svec_init(&bindings);
     SHASH_FOR_EACH (node, &port->bindings) {
         struct vtep_ctl_lswitch *lswitch = node->data;
         char *binding;
-        
+
         binding = xasprintf("%04lld %s", strtoll(node->name, NULL, 0),
                             lswitch->name);
         svec_add_nocopy(&bindings, binding);
@@ -1684,17 +1390,18 @@ cmd_list_bindings(struct vtep_ctl_context *ctx)
 }
 
 static void
-cmd_bind_ls(struct vtep_ctl_context *ctx)
+cmd_bind_ls(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct vtep_ctl_lswitch *ls;
     struct vtep_ctl_port *port;
     const char *vlan;
 
     vtep_ctl_context_populate_cache(ctx);
 
-    port = find_port(ctx, ctx->argv[1], ctx->argv[2], true);
+    port = find_port(vtepctl_ctx, ctx->argv[1], ctx->argv[2], true);
     vlan = ctx->argv[3];
-    ls = find_lswitch(ctx, ctx->argv[4], true);
+    ls = find_lswitch(vtepctl_ctx, ctx->argv[4], true);
 
     add_ls_binding_to_cache(port, vlan, ls);
     commit_ls_bindings(port);
@@ -1703,14 +1410,15 @@ cmd_bind_ls(struct vtep_ctl_context *ctx)
 }
 
 static void
-cmd_unbind_ls(struct vtep_ctl_context *ctx)
+cmd_unbind_ls(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct vtep_ctl_port *port;
     const char *vlan;
 
     vtep_ctl_context_populate_cache(ctx);
 
-    port = find_port(ctx, ctx->argv[1], ctx->argv[2], true);
+    port = find_port(vtepctl_ctx, ctx->argv[1], ctx->argv[2], true);
     vlan = ctx->argv[3];
 
     del_cached_ls_binding(port, vlan);
@@ -1720,8 +1428,9 @@ cmd_unbind_ls(struct vtep_ctl_context *ctx)
 }
 
 static void
-add_ucast_entry(struct vtep_ctl_context *ctx, bool local)
+add_ucast_entry(struct ctl_context *ctx, bool local)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct vtep_ctl_lswitch *ls;
     const char *mac;
     const char *encap;
@@ -1730,7 +1439,7 @@ add_ucast_entry(struct vtep_ctl_context *ctx, bool local)
 
     vtep_ctl_context_populate_cache(ctx);
 
-    ls = find_lswitch(ctx, ctx->argv[1], true);
+    ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true);
     mac = ctx->argv[2];
 
     if (ctx->argc == 4) {
@@ -1741,13 +1450,13 @@ add_ucast_entry(struct vtep_ctl_context *ctx, bool local)
         dst_ip = ctx->argv[4];
     }
 
-    ploc_cfg = find_ploc(ctx, encap, dst_ip);
+    ploc_cfg = find_ploc(vtepctl_ctx, encap, dst_ip);
     if (!ploc_cfg) {
         ploc_cfg = vteprec_physical_locator_insert(ctx->txn);
         vteprec_physical_locator_set_dst_ip(ploc_cfg, dst_ip);
         vteprec_physical_locator_set_encapsulation_type(ploc_cfg, encap);
 
-        add_ploc_to_cache(ctx, ploc_cfg);
+        add_ploc_to_cache(vtepctl_ctx, ploc_cfg);
     }
 
     if (local) {
@@ -1778,27 +1487,28 @@ add_ucast_entry(struct vtep_ctl_context *ctx, bool local)
 }
 
 static void
-cmd_add_ucast_local(struct vtep_ctl_context *ctx)
+cmd_add_ucast_local(struct ctl_context *ctx)
 {
     add_ucast_entry(ctx, true);
 }
 
 static void
-cmd_add_ucast_remote(struct vtep_ctl_context *ctx)
+cmd_add_ucast_remote(struct ctl_context *ctx)
 {
     add_ucast_entry(ctx, false);
 }
 
 static void
-del_ucast_entry(struct vtep_ctl_context *ctx, bool local)
+del_ucast_entry(struct ctl_context *ctx, bool local)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct vtep_ctl_lswitch *ls;
     struct shash *ucast_shash;
     struct shash_node *node;
 
     vtep_ctl_context_populate_cache(ctx);
 
-    ls = find_lswitch(ctx, ctx->argv[1], true);
+    ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true);
     ucast_shash = local ? &ls->ucast_local : &ls->ucast_remote;
 
     node = shash_find(ucast_shash, ctx->argv[2]);
@@ -1819,13 +1529,13 @@ del_ucast_entry(struct vtep_ctl_context *ctx, bool local)
 }
 
 static void
-cmd_del_ucast_local(struct vtep_ctl_context *ctx)
+cmd_del_ucast_local(struct ctl_context *ctx)
 {
     del_ucast_entry(ctx, true);
 }
 
 static void
-cmd_del_ucast_remote(struct vtep_ctl_context *ctx)
+cmd_del_ucast_remote(struct ctl_context *ctx)
 {
     del_ucast_entry(ctx, false);
 }
@@ -1857,10 +1567,11 @@ commit_mcast_entries(struct vtep_ctl_mcast_mac *mcast_mac)
 }
 
 static void
-add_mcast_entry(struct vtep_ctl_context *ctx,
+add_mcast_entry(struct ctl_context *ctx,
                 struct vtep_ctl_lswitch *ls, const char *mac,
                 const char *encap, const char *dst_ip, bool local)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct shash *mcast_shash;
     struct vtep_ctl_mcast_mac *mcast_mac;
     struct vteprec_physical_locator *ploc_cfg;
@@ -1873,7 +1584,8 @@ add_mcast_entry(struct vtep_ctl_context *ctx,
 
     mcast_mac = shash_find_data(mcast_shash, mac);
     if (!mcast_mac) {
-        mcast_mac = add_mcast_mac_to_cache(ctx, ls, mac, ploc_set_cfg, local);
+        mcast_mac = add_mcast_mac_to_cache(vtepctl_ctx, ls, mac, ploc_set_cfg,
+                                           local);
 
         if (local) {
             mcast_mac->local_cfg = vteprec_mcast_macs_local_insert(ctx->txn);
@@ -1903,13 +1615,13 @@ add_mcast_entry(struct vtep_ctl_context *ctx,
         }
     }
 
-    ploc_cfg = find_ploc(ctx, encap, dst_ip);
+    ploc_cfg = find_ploc(vtepctl_ctx, encap, dst_ip);
     if (!ploc_cfg) {
         ploc_cfg = vteprec_physical_locator_insert(ctx->txn);
         vteprec_physical_locator_set_dst_ip(ploc_cfg, dst_ip);
         vteprec_physical_locator_set_encapsulation_type(ploc_cfg, encap);
 
-        add_ploc_to_cache(ctx, ploc_cfg);
+        add_ploc_to_cache(vtepctl_ctx, ploc_cfg);
     }
 
     add_ploc_to_mcast_mac(mcast_mac, ploc_cfg);
@@ -1917,10 +1629,11 @@ add_mcast_entry(struct vtep_ctl_context *ctx,
 }
 
 static void
-del_mcast_entry(struct vtep_ctl_context *ctx,
+del_mcast_entry(struct ctl_context *ctx,
                 struct vtep_ctl_lswitch *ls, const char *mac,
                 const char *encap, const char *dst_ip, bool local)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct vtep_ctl_mcast_mac *mcast_mac;
     struct shash *mcast_shash;
     struct vteprec_physical_locator *ploc_cfg;
@@ -1933,7 +1646,7 @@ del_mcast_entry(struct vtep_ctl_context *ctx,
         return;
     }
 
-    ploc_cfg = find_ploc(ctx, encap, dst_ip);
+    ploc_cfg = find_ploc(vtepctl_ctx, encap, dst_ip);
     if (!ploc_cfg) {
         /* Couldn't find the physical locator, so just ignore. */
         return;
@@ -1954,7 +1667,7 @@ del_mcast_entry(struct vtep_ctl_context *ctx,
         } else {
             vteprec_mcast_macs_remote_delete(mcast_mac->remote_cfg);
         }
-        
+
         free(node->data);
         shash_delete(mcast_shash, node);
     } else {
@@ -1970,8 +1683,9 @@ del_mcast_entry(struct vtep_ctl_context *ctx,
 }
 
 static void
-add_del_mcast_entry(struct vtep_ctl_context *ctx, bool add, bool local)
+add_del_mcast_entry(struct ctl_context *ctx, bool add, bool local)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct vtep_ctl_lswitch *ls;
     const char *mac;
     const char *encap;
@@ -1979,7 +1693,7 @@ add_del_mcast_entry(struct vtep_ctl_context *ctx, bool add, bool local)
 
     vtep_ctl_context_populate_cache(ctx);
 
-    ls = find_lswitch(ctx, ctx->argv[1], true);
+    ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true);
     mac = ctx->argv[2];
 
     if (ctx->argc == 4) {
@@ -2000,39 +1714,40 @@ add_del_mcast_entry(struct vtep_ctl_context *ctx, bool add, bool local)
 }
 
 static void
-cmd_add_mcast_local(struct vtep_ctl_context *ctx)
+cmd_add_mcast_local(struct ctl_context *ctx)
 {
     add_del_mcast_entry(ctx, true, true);
 }
 
 static void
-cmd_add_mcast_remote(struct vtep_ctl_context *ctx)
+cmd_add_mcast_remote(struct ctl_context *ctx)
 {
     add_del_mcast_entry(ctx, true, false);
 }
 
 static void
-cmd_del_mcast_local(struct vtep_ctl_context *ctx)
+cmd_del_mcast_local(struct ctl_context *ctx)
 {
     add_del_mcast_entry(ctx, false, true);
 }
 
 static void
-cmd_del_mcast_remote(struct vtep_ctl_context *ctx)
+cmd_del_mcast_remote(struct ctl_context *ctx)
 {
     add_del_mcast_entry(ctx, false, false);
 }
 
 static void
-clear_macs(struct vtep_ctl_context *ctx, bool local)
+clear_macs(struct ctl_context *ctx, bool local)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct vtep_ctl_lswitch *ls;
     const struct shash_node *node;
     struct shash *ucast_shash;
     struct shash *mcast_shash;
 
     vtep_ctl_context_populate_cache(ctx);
-    ls = find_lswitch(ctx, ctx->argv[1], true);
+    ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true);
 
     ucast_shash = local ? &ls->ucast_local : &ls->ucast_remote;
     mcast_shash = local ? &ls->mcast_local : &ls->mcast_remote;
@@ -2060,20 +1775,21 @@ clear_macs(struct vtep_ctl_context *ctx, bool local)
 }
 
 static void
-cmd_clear_local_macs(struct vtep_ctl_context *ctx)
+cmd_clear_local_macs(struct ctl_context *ctx)
 {
     clear_macs(ctx, true);
 }
 
 static void
-cmd_clear_remote_macs(struct vtep_ctl_context *ctx)
+cmd_clear_remote_macs(struct ctl_context *ctx)
 {
     clear_macs(ctx, false);
 }
 
 static void
-list_macs(struct vtep_ctl_context *ctx, bool local)
+list_macs(struct ctl_context *ctx, bool local)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     struct vtep_ctl_lswitch *ls;
     const struct shash_node *node;
     struct shash *ucast_shash;
@@ -2082,7 +1798,7 @@ list_macs(struct vtep_ctl_context *ctx, bool local)
     struct svec mcast_macs;
 
     vtep_ctl_context_populate_cache(ctx);
-    ls = find_lswitch(ctx, ctx->argv[1], true);
+    ls = find_lswitch(vtepctl_ctx, ctx->argv[1], true);
 
     ucast_shash = local ? &ls->ucast_local : &ls->ucast_remote;
     mcast_shash = local ? &ls->mcast_local : &ls->mcast_remote;
@@ -2125,13 +1841,13 @@ list_macs(struct vtep_ctl_context *ctx, bool local)
 }
 
 static void
-cmd_list_local_macs(struct vtep_ctl_context *ctx)
+cmd_list_local_macs(struct ctl_context *ctx)
 {
     list_macs(ctx, true);
 }
 
 static void
-cmd_list_remote_macs(struct vtep_ctl_context *ctx)
+cmd_list_remote_macs(struct ctl_context *ctx)
 {
     list_macs(ctx, false);
 }
@@ -2151,16 +1867,17 @@ verify_managers(const struct vteprec_global *vtep_global)
 }
 
 static void
-pre_manager(struct vtep_ctl_context *ctx)
+pre_manager(struct ctl_context *ctx)
 {
     ovsdb_idl_add_column(ctx->idl, &vteprec_global_col_managers);
     ovsdb_idl_add_column(ctx->idl, &vteprec_manager_col_target);
 }
 
 static void
-cmd_get_manager(struct vtep_ctl_context *ctx)
+cmd_get_manager(struct ctl_context *ctx)
 {
-    const struct vteprec_global *vtep_global = ctx->vtep_global;
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
+    const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global;
     struct svec targets;
     size_t i;
 
@@ -2181,9 +1898,9 @@ cmd_get_manager(struct vtep_ctl_context *ctx)
 }
 
 static void
-delete_managers(const struct vtep_ctl_context *ctx)
+delete_managers(const struct vtep_ctl_context *vtepctl_ctx)
 {
-    const struct vteprec_global *vtep_global = ctx->vtep_global;
+    const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global;
     size_t i;
 
     /* Delete Manager rows pointed to by 'managers' column. */
@@ -2196,16 +1913,17 @@ delete_managers(const struct vtep_ctl_context *ctx)
 }
 
 static void
-cmd_del_manager(struct vtep_ctl_context *ctx)
+cmd_del_manager(struct ctl_context *ctx)
 {
-    const struct vteprec_global *vtep_global = ctx->vtep_global;
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
+    const struct vteprec_global *vtep_global = vtepctl_ctx->vtep_global;
 
     verify_managers(vtep_global);
-    delete_managers(ctx);
+    delete_managers(vtepctl_ctx);
 }
 
 static void
-insert_managers(struct vtep_ctl_context *ctx, char *targets[], size_t n)
+insert_managers(struct vtep_ctl_context *vtepctl_ctx, char *targets[], size_t n)
 {
     struct vteprec_manager **managers;
     size_t i;
@@ -2216,39 +1934,28 @@ insert_managers(struct vtep_ctl_context *ctx, char *targets[], size_t n)
         if (stream_verify_name(targets[i]) && pstream_verify_name(targets[i])) {
             VLOG_WARN("target type \"%s\" is possibly erroneous", targets[i]);
         }
-        managers[i] = vteprec_manager_insert(ctx->txn);
+        managers[i] = vteprec_manager_insert(vtepctl_ctx->base.txn);
         vteprec_manager_set_target(managers[i], targets[i]);
     }
 
     /* Store uuids of new Manager rows in 'managers' column. */
-    vteprec_global_set_managers(ctx->vtep_global, managers, n);
+    vteprec_global_set_managers(vtepctl_ctx->vtep_global, managers, n);
     free(managers);
 }
 
 static void
-cmd_set_manager(struct vtep_ctl_context *ctx)
+cmd_set_manager(struct ctl_context *ctx)
 {
+    struct vtep_ctl_context *vtepctl_ctx = vtep_ctl_context_cast(ctx);
     const size_t n = ctx->argc - 1;
 
-    verify_managers(ctx->vtep_global);
-    delete_managers(ctx);
-    insert_managers(ctx, &ctx->argv[1], n);
+    verify_managers(vtepctl_ctx->vtep_global);
+    delete_managers(vtepctl_ctx);
+    insert_managers(vtepctl_ctx, &ctx->argv[1], n);
 }
 
 /* Parameter commands. */
-
-struct vtep_ctl_row_id {
-    const struct ovsdb_idl_table_class *table;
-    const struct ovsdb_idl_column *name_column;
-    const struct ovsdb_idl_column *uuid_column;
-};
-
-struct vtep_ctl_table_class {
-    struct ovsdb_idl_table_class *class;
-    struct vtep_ctl_row_id row_ids[2];
-};
-
-static const struct vtep_ctl_table_class tables[] = {
+const struct ctl_table_class tables[] = {
     {&vteprec_table_global,
      {{&vteprec_table_global, NULL, NULL},
       {NULL, NULL, NULL}}},
@@ -2304,1478 +2011,172 @@ static const struct vtep_ctl_table_class tables[] = {
     {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
 };
 
+\f
 static void
-die_if_error(char *error)
+vtep_ctl_context_init_command(struct vtep_ctl_context *vtepctl_ctx,
+                              struct ctl_command *command)
 {
-    if (error) {
-        vtep_ctl_fatal("%s", error);
-    }
+    ctl_context_init_command(&vtepctl_ctx->base, command);
+    vtepctl_ctx->verified_ports = false;
+
 }
 
-static int
-to_lower_and_underscores(unsigned c)
+static void
+vtep_ctl_context_init(struct vtep_ctl_context *vtepctl_ctx,
+                      struct ctl_command *command,
+                      struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn,
+                      const struct vteprec_global *vtep_global,
+                      struct ovsdb_symbol_table *symtab)
 {
-    return c == '-' ? '_' : tolower(c);
+    ctl_context_init(&vtepctl_ctx->base, command, idl, txn, symtab,
+                     vtep_ctl_context_invalidate_cache);
+    if (command) {
+        vtepctl_ctx->verified_ports = false;
+    }
+    vtepctl_ctx->vtep_global = vtep_global;
+    vtepctl_ctx->cache_valid = false;
 }
 
-static unsigned int
-score_partial_match(const char *name, const char *s)
+static void
+vtep_ctl_context_done_command(struct vtep_ctl_context *vtepctl_ctx,
+                              struct ctl_command *command)
 {
-    int score;
+    ctl_context_done_command(&vtepctl_ctx->base, command);
+}
 
-    if (!strcmp(name, s)) {
-        return UINT_MAX;
-    }
-    for (score = 0; ; score++, name++, s++) {
-        if (to_lower_and_underscores(*name) != to_lower_and_underscores(*s)) {
-            break;
-        } else if (*name == '\0') {
-            return UINT_MAX - 1;
-        }
-    }
-    return *s == '\0' ? score : 0;
+static void
+vtep_ctl_context_done(struct vtep_ctl_context *vtepctl_ctx,
+                      struct ctl_command *command)
+{
+    ctl_context_done(&vtepctl_ctx->base, command);
 }
 
-static const struct vtep_ctl_table_class *
-get_table(const char *table_name)
+static void
+run_prerequisites(struct ctl_command *commands, size_t n_commands,
+                  struct ovsdb_idl *idl)
 {
-    const struct vtep_ctl_table_class *table;
-    const struct vtep_ctl_table_class *best_match = NULL;
-    unsigned int best_score = 0;
+    struct ctl_command *c;
+
+    ovsdb_idl_add_table(idl, &vteprec_table_global);
+    for (c = commands; c < &commands[n_commands]; c++) {
+        if (c->syntax->prerequisites) {
+            struct vtep_ctl_context vtepctl_ctx;
+
+            ds_init(&c->output);
+            c->table = NULL;
 
-    for (table = tables; table->class; table++) {
-        unsigned int score = score_partial_match(table->class->name,
-                                                 table_name);
-        if (score > best_score) {
-            best_match = table;
-            best_score = score;
-        } else if (score == best_score) {
-            best_match = NULL;
+            vtep_ctl_context_init(&vtepctl_ctx, c, idl, NULL, NULL, NULL);
+            (c->syntax->prerequisites)(&vtepctl_ctx.base);
+            vtep_ctl_context_done(&vtepctl_ctx, c);
+
+            ovs_assert(!c->output.string);
+            ovs_assert(!c->table);
         }
     }
-    if (best_match) {
-        return best_match;
-    } else if (best_score) {
-        vtep_ctl_fatal("multiple table names match \"%s\"", table_name);
-    } else {
-        vtep_ctl_fatal("unknown table \"%s\"", table_name);
-    }
 }
 
-static const struct vtep_ctl_table_class *
-pre_get_table(struct vtep_ctl_context *ctx, const char *table_name)
+static void
+do_vtep_ctl(const char *args, struct ctl_command *commands,
+            size_t n_commands, struct ovsdb_idl *idl)
 {
-    const struct vtep_ctl_table_class *table_class;
-    int i;
-
-    table_class = get_table(table_name);
-    ovsdb_idl_add_table(ctx->idl, table_class->class);
+    struct ovsdb_idl_txn *txn;
+    const struct vteprec_global *vtep_global;
+    enum ovsdb_idl_txn_status status;
+    struct ovsdb_symbol_table *symtab;
+    struct vtep_ctl_context vtepctl_ctx;
+    struct ctl_command *c;
+    struct shash_node *node;
+    char *error = NULL;
 
-    for (i = 0; i < ARRAY_SIZE(table_class->row_ids); i++) {
-        const struct vtep_ctl_row_id *id = &table_class->row_ids[i];
-        if (id->table) {
-            ovsdb_idl_add_table(ctx->idl, id->table);
-        }
-        if (id->name_column) {
-            ovsdb_idl_add_column(ctx->idl, id->name_column);
-        }
-        if (id->uuid_column) {
-            ovsdb_idl_add_column(ctx->idl, id->uuid_column);
-        }
+    txn = the_idl_txn = ovsdb_idl_txn_create(idl);
+    if (dry_run) {
+        ovsdb_idl_txn_set_dry_run(txn);
     }
 
-    return table_class;
-}
+    ovsdb_idl_txn_add_comment(txn, "vtep-ctl: %s", args);
 
-static const struct ovsdb_idl_row *
-get_row_by_id(struct vtep_ctl_context *ctx, const struct vtep_ctl_table_class *table,
-              const struct vtep_ctl_row_id *id, const char *record_id)
-{
-    const struct ovsdb_idl_row *referrer, *final;
+    vtep_global = vteprec_global_first(idl);
+    if (!vtep_global) {
+        /* XXX add verification that table is empty */
+        vtep_global = vteprec_global_insert(txn);
+    }
 
-    if (!id->table) {
-        return NULL;
+    symtab = ovsdb_symbol_table_create();
+    for (c = commands; c < &commands[n_commands]; c++) {
+        ds_init(&c->output);
+        c->table = NULL;
     }
+    vtep_ctl_context_init(&vtepctl_ctx, NULL, idl, txn, vtep_global, symtab);
+    for (c = commands; c < &commands[n_commands]; c++) {
+        vtep_ctl_context_init_command(&vtepctl_ctx, c);
+        if (c->syntax->run) {
+            (c->syntax->run)(&vtepctl_ctx.base);
+        }
+        vtep_ctl_context_done_command(&vtepctl_ctx, c);
 
-    if (!id->name_column) {
-        if (strcmp(record_id, ".")) {
-            return NULL;
+        if (vtepctl_ctx.base.try_again) {
+            vtep_ctl_context_done(&vtepctl_ctx, NULL);
+            goto try_again;
         }
-        referrer = ovsdb_idl_first_row(ctx->idl, id->table);
-        if (!referrer || ovsdb_idl_next_row(referrer)) {
-            return NULL;
+    }
+    vtep_ctl_context_done(&vtepctl_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);
         }
-    } else {
-        const struct ovsdb_idl_row *row;
-
-        referrer = NULL;
-        for (row = ovsdb_idl_first_row(ctx->idl, id->table);
-             row != NULL;
-             row = ovsdb_idl_next_row(row))
-        {
-            const struct ovsdb_datum *name;
-
-            name = ovsdb_idl_get(row, id->name_column,
-                                 OVSDB_TYPE_STRING, OVSDB_TYPE_VOID);
-            if (name->n == 1 && !strcmp(name->keys[0].string, record_id)) {
-                if (referrer) {
-                    vtep_ctl_fatal("multiple rows in %s match \"%s\"",
-                                table->class->name, record_id);
-                }
-                referrer = row;
+        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);
             }
         }
     }
-    if (!referrer) {
-        return NULL;
-    }
 
-    final = NULL;
-    if (id->uuid_column) {
-        const struct ovsdb_datum *uuid;
-
-        ovsdb_idl_txn_verify(referrer, id->uuid_column);
-        uuid = ovsdb_idl_get(referrer, id->uuid_column,
-                             OVSDB_TYPE_UUID, OVSDB_TYPE_VOID);
-        if (uuid->n == 1) {
-            final = ovsdb_idl_get_row_for_uuid(ctx->idl, table->class,
-                                               &uuid->keys[0].uuid);
+    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) {
+                vtep_ctl_context_init(&vtepctl_ctx, c, idl, txn, vtep_global, symtab);
+                (c->syntax->postprocess)(&vtepctl_ctx.base);
+                vtep_ctl_context_done(&vtepctl_ctx, c);
+            }
         }
-    } else {
-        final = referrer;
     }
+    error = xstrdup(ovsdb_idl_txn_get_error(txn));
+    ovsdb_idl_txn_destroy(txn);
+    txn = the_idl_txn = NULL;
 
-    return final;
-}
+    switch (status) {
+    case TXN_UNCOMMITTED:
+    case TXN_INCOMPLETE:
+        OVS_NOT_REACHED();
 
-static const struct ovsdb_idl_row *
-get_row (struct vtep_ctl_context *ctx,
-         const struct vtep_ctl_table_class *table, const char *record_id)
-{
-    const struct ovsdb_idl_row *row;
-    struct uuid uuid;
+    case TXN_ABORTED:
+        /* Should not happen--we never call ovsdb_idl_txn_abort(). */
+        ctl_fatal("transaction aborted");
 
-    if (uuid_from_string(&uuid, record_id)) {
-        row = ovsdb_idl_get_row_for_uuid(ctx->idl, table->class, &uuid);
-    } else {
-        int i;
+    case TXN_UNCHANGED:
+    case TXN_SUCCESS:
+        break;
 
-        for (i = 0; i < ARRAY_SIZE(table->row_ids); i++) {
-            row = get_row_by_id(ctx, table, &table->row_ids[i], record_id);
-            if (row) {
-                break;
-            }
-        }
-    }
-    return row;
-}
+    case TXN_TRY_AGAIN:
+        goto try_again;
 
-static const struct ovsdb_idl_row *
-must_get_row(struct vtep_ctl_context *ctx,
-             const struct vtep_ctl_table_class *table, const char *record_id)
-{
-    const struct ovsdb_idl_row *row = get_row(ctx, table, record_id);
-    if (!row) {
-        vtep_ctl_fatal("no row \"%s\" in table %s",
-                    record_id, table->class->name);
-    }
-    return row;
-}
+    case TXN_ERROR:
+        ctl_fatal("transaction error: %s", error);
 
-static char *
-get_column(const struct vtep_ctl_table_class *table, const char *column_name,
-           const struct ovsdb_idl_column **columnp)
-{
-    const struct ovsdb_idl_column *best_match = NULL;
-    unsigned int best_score = 0;
-    size_t i;
-
-    for (i = 0; i < table->class->n_columns; i++) {
-        const struct ovsdb_idl_column *column = &table->class->columns[i];
-        unsigned int score = score_partial_match(column->name, column_name);
-        if (score > best_score) {
-            best_match = column;
-            best_score = score;
-        } else if (score == best_score) {
-            best_match = NULL;
-        }
-    }
-
-    *columnp = best_match;
-    if (best_match) {
-        return NULL;
-    } else if (best_score) {
-        return xasprintf("%s contains more than one column whose name "
-                         "matches \"%s\"", table->class->name, column_name);
-    } else {
-        return xasprintf("%s does not contain a column whose name matches "
-                         "\"%s\"", table->class->name, column_name);
-    }
-}
-
-static struct ovsdb_symbol *
-create_symbol(struct ovsdb_symbol_table *symtab, const char *id, bool *newp)
-{
-    struct ovsdb_symbol *symbol;
-
-    if (id[0] != '@') {
-        vtep_ctl_fatal("row id \"%s\" does not begin with \"@\"", id);
-    }
-
-    if (newp) {
-        *newp = ovsdb_symbol_table_get(symtab, id) == NULL;
-    }
-
-    symbol = ovsdb_symbol_table_insert(symtab, id);
-    if (symbol->created) {
-        vtep_ctl_fatal("row id \"%s\" may only be specified on one --id option",
-                    id);
-    }
-    symbol->created = true;
-    return symbol;
-}
-
-static void
-pre_get_column(struct vtep_ctl_context *ctx,
-               const struct vtep_ctl_table_class *table, const char *column_name,
-               const struct ovsdb_idl_column **columnp)
-{
-    die_if_error(get_column(table, column_name, columnp));
-    ovsdb_idl_add_column(ctx->idl, *columnp);
-}
-
-static char *
-missing_operator_error(const char *arg, const char **allowed_operators,
-                       size_t n_allowed)
-{
-    struct ds s;
-
-    ds_init(&s);
-    ds_put_format(&s, "%s: argument does not end in ", arg);
-    ds_put_format(&s, "\"%s\"", allowed_operators[0]);
-    if (n_allowed == 2) {
-        ds_put_format(&s, " or \"%s\"", allowed_operators[1]);
-    } else if (n_allowed > 2) {
-        size_t i;
-
-        for (i = 1; i < n_allowed - 1; i++) {
-            ds_put_format(&s, ", \"%s\"", allowed_operators[i]);
-        }
-        ds_put_format(&s, ", or \"%s\"", allowed_operators[i]);
-    }
-    ds_put_format(&s, " followed by a value.");
-
-    return ds_steal_cstr(&s);
-}
-
-/* Breaks 'arg' apart into a number of fields in the following order:
- *
- *      - The name of a column in 'table', stored into '*columnp'.  The column
- *        name may be abbreviated.
- *
- *      - Optionally ':' followed by a key string.  The key is stored as a
- *        malloc()'d string into '*keyp', or NULL if no key is present in
- *        'arg'.
- *
- *      - If 'valuep' is nonnull, an operator followed by a value string.  The
- *        allowed operators are the 'n_allowed' string in 'allowed_operators',
- *        or just "=" if 'n_allowed' is 0.  If 'operatorp' is nonnull, then the
- *        index of the operator within 'allowed_operators' is stored into
- *        '*operatorp'.  The value is stored as a malloc()'d string into
- *        '*valuep', or NULL if no value is present in 'arg'.
- *
- * On success, returns NULL.  On failure, returned a malloc()'d string error
- * message and stores NULL into all of the nonnull output arguments. */
-static char * OVS_WARN_UNUSED_RESULT
-parse_column_key_value(const char *arg,
-                       const struct vtep_ctl_table_class *table,
-                       const struct ovsdb_idl_column **columnp, char **keyp,
-                       int *operatorp,
-                       const char **allowed_operators, size_t n_allowed,
-                       char **valuep)
-{
-    const char *p = arg;
-    char *column_name;
-    char *error;
-
-    ovs_assert(!(operatorp && !valuep));
-    *keyp = NULL;
-    if (valuep) {
-        *valuep = NULL;
-    }
-
-    /* Parse column name. */
-    error = ovsdb_token_parse(&p, &column_name);
-    if (error) {
-        goto error;
-    }
-    if (column_name[0] == '\0') {
-        free(column_name);
-        error = xasprintf("%s: missing column name", arg);
-        goto error;
-    }
-    error = get_column(table, column_name, columnp);
-    free(column_name);
-    if (error) {
-        goto error;
-    }
-
-    /* Parse key string. */
-    if (*p == ':') {
-        p++;
-        error = ovsdb_token_parse(&p, keyp);
-        if (error) {
-            goto error;
-        }
-    }
-
-    /* Parse value string. */
-    if (valuep) {
-        size_t best_len;
-        size_t i;
-        int best;
-
-        if (!allowed_operators) {
-            static const char *equals = "=";
-            allowed_operators = &equals;
-            n_allowed = 1;
-        }
-
-        best = -1;
-        best_len = 0;
-        for (i = 0; i < n_allowed; i++) {
-            const char *op = allowed_operators[i];
-            size_t op_len = strlen(op);
-
-            if (op_len > best_len && !strncmp(op, p, op_len) && p[op_len]) {
-                best_len = op_len;
-                best = i;
-            }
-        }
-        if (best < 0) {
-            error = missing_operator_error(arg, allowed_operators, n_allowed);
-            goto error;
-        }
-
-        if (operatorp) {
-            *operatorp = best;
-        }
-        *valuep = xstrdup(p + best_len);
-    } else {
-        if (*p != '\0') {
-            error = xasprintf("%s: trailing garbage \"%s\" in argument",
-                              arg, p);
-            goto error;
-        }
-    }
-    return NULL;
-
-error:
-    *columnp = NULL;
-    free(*keyp);
-    *keyp = NULL;
-    if (valuep) {
-        free(*valuep);
-        *valuep = NULL;
-        if (operatorp) {
-            *operatorp = -1;
-        }
-    }
-    return error;
-}
-
-static const struct ovsdb_idl_column *
-pre_parse_column_key_value(struct vtep_ctl_context *ctx,
-                           const char *arg,
-                           const struct vtep_ctl_table_class *table)
-{
-    const struct ovsdb_idl_column *column;
-    const char *p;
-    char *column_name;
-
-    p = arg;
-    die_if_error(ovsdb_token_parse(&p, &column_name));
-    if (column_name[0] == '\0') {
-        vtep_ctl_fatal("%s: missing column name", arg);
-    }
-
-    pre_get_column(ctx, table, column_name, &column);
-    free(column_name);
-
-    return column;
-}
-
-static void
-check_mutable(const struct vtep_ctl_table_class *table,
-              const struct ovsdb_idl_column *column)
-{
-    if (!column->mutable) {
-        vtep_ctl_fatal("cannot modify read-only column %s in table %s",
-                    column->name, table->class->name);
-    }
-}
-
-static void
-pre_cmd_get(struct vtep_ctl_context *ctx)
-{
-    const char *id = shash_find_data(&ctx->options, "--id");
-    const char *table_name = ctx->argv[1];
-    const struct vtep_ctl_table_class *table;
-    int i;
-
-    /* Using "get" without --id or a column name could possibly make sense.
-     * Maybe, for example, a vtep-ctl run wants to assert that a row exists.
-     * But it is unlikely that an interactive user would want to do that, so
-     * issue a warning if we're running on a terminal. */
-    if (!id && ctx->argc <= 3 && isatty(STDOUT_FILENO)) {
-        VLOG_WARN("\"get\" command without row arguments or \"--id\" is "
-                  "possibly erroneous");
-    }
-
-    table = pre_get_table(ctx, table_name);
-    for (i = 3; i < ctx->argc; i++) {
-        if (!strcasecmp(ctx->argv[i], "_uuid")
-            || !strcasecmp(ctx->argv[i], "-uuid")) {
-            continue;
-        }
-
-        pre_parse_column_key_value(ctx, ctx->argv[i], table);
-    }
-}
-
-static void
-cmd_get(struct vtep_ctl_context *ctx)
-{
-    const char *id = shash_find_data(&ctx->options, "--id");
-    bool if_exists = shash_find(&ctx->options, "--if-exists");
-    const char *table_name = ctx->argv[1];
-    const char *record_id = ctx->argv[2];
-    const struct vtep_ctl_table_class *table;
-    const struct ovsdb_idl_row *row;
-    struct ds *out = &ctx->output;
-    int i;
-
-    table = get_table(table_name);
-    row = must_get_row(ctx, table, record_id);
-    if (id) {
-        struct ovsdb_symbol *symbol;
-        bool new;
-
-        symbol = create_symbol(ctx->symtab, id, &new);
-        if (!new) {
-            vtep_ctl_fatal("row id \"%s\" specified on \"get\" command was used "
-                        "before it was defined", id);
-        }
-        symbol->uuid = row->uuid;
-
-        /* This symbol refers to a row that already exists, so disable warnings
-         * about it being unreferenced. */
-        symbol->strong_ref = true;
-    }
-    for (i = 3; i < ctx->argc; i++) {
-        const struct ovsdb_idl_column *column;
-        const struct ovsdb_datum *datum;
-        char *key_string;
-
-        /* Special case for obtaining the UUID of a row.  We can't just do this
-         * through parse_column_key_value() below since it returns a "struct
-         * ovsdb_idl_column" and the UUID column doesn't have one. */
-        if (!strcasecmp(ctx->argv[i], "_uuid")
-            || !strcasecmp(ctx->argv[i], "-uuid")) {
-            ds_put_format(out, UUID_FMT"\n", UUID_ARGS(&row->uuid));
-            continue;
-        }
-
-        die_if_error(parse_column_key_value(ctx->argv[i], table,
-                                            &column, &key_string,
-                                            NULL, NULL, 0, NULL));
-
-        ovsdb_idl_txn_verify(row, column);
-        datum = ovsdb_idl_read(row, column);
-        if (key_string) {
-            union ovsdb_atom key;
-            unsigned int idx;
-
-            if (column->type.value.type == OVSDB_TYPE_VOID) {
-                vtep_ctl_fatal("cannot specify key to get for non-map column %s",
-                            column->name);
-            }
-
-            die_if_error(ovsdb_atom_from_string(&key,
-                                                &column->type.key,
-                                                key_string, ctx->symtab));
-
-            idx = ovsdb_datum_find_key(datum, &key,
-                                       column->type.key.type);
-            if (idx == UINT_MAX) {
-                if (!if_exists) {
-                    vtep_ctl_fatal("no key \"%s\" in %s record \"%s\" column %s",
-                                key_string, table->class->name, record_id,
-                                column->name);
-                }
-            } else {
-                ovsdb_atom_to_string(&datum->values[idx],
-                                     column->type.value.type, out);
-            }
-            ovsdb_atom_destroy(&key, column->type.key.type);
-        } else {
-            ovsdb_datum_to_string(datum, &column->type, out);
-        }
-        ds_put_char(out, '\n');
-
-        free(key_string);
-    }
-}
-
-static void
-parse_column_names(const char *column_names,
-                   const struct vtep_ctl_table_class *table,
-                   const struct ovsdb_idl_column ***columnsp,
-                   size_t *n_columnsp)
-{
-    const struct ovsdb_idl_column **columns;
-    size_t n_columns;
-
-    if (!column_names) {
-        size_t i;
-
-        n_columns = table->class->n_columns + 1;
-        columns = xmalloc(n_columns * sizeof *columns);
-        columns[0] = NULL;
-        for (i = 0; i < table->class->n_columns; i++) {
-            columns[i + 1] = &table->class->columns[i];
-        }
-    } else {
-        char *s = xstrdup(column_names);
-        size_t allocated_columns;
-        char *save_ptr = NULL;
-        char *column_name;
-
-        columns = NULL;
-        allocated_columns = n_columns = 0;
-        for (column_name = strtok_r(s, ", ", &save_ptr); column_name;
-             column_name = strtok_r(NULL, ", ", &save_ptr)) {
-            const struct ovsdb_idl_column *column;
-
-            if (!strcasecmp(column_name, "_uuid")) {
-                column = NULL;
-            } else {
-                die_if_error(get_column(table, column_name, &column));
-            }
-            if (n_columns >= allocated_columns) {
-                columns = x2nrealloc(columns, &allocated_columns,
-                                     sizeof *columns);
-            }
-            columns[n_columns++] = column;
-        }
-        free(s);
-
-        if (!n_columns) {
-            vtep_ctl_fatal("must specify at least one column name");
-        }
-    }
-    *columnsp = columns;
-    *n_columnsp = n_columns;
-}
-
-
-static void
-pre_list_columns(struct vtep_ctl_context *ctx,
-                 const struct vtep_ctl_table_class *table,
-                 const char *column_names)
-{
-    const struct ovsdb_idl_column **columns;
-    size_t n_columns;
-    size_t i;
-
-    parse_column_names(column_names, table, &columns, &n_columns);
-    for (i = 0; i < n_columns; i++) {
-        if (columns[i]) {
-            ovsdb_idl_add_column(ctx->idl, columns[i]);
-        }
-    }
-    free(columns);
-}
-
-static void
-pre_cmd_list(struct vtep_ctl_context *ctx)
-{
-    const char *column_names = shash_find_data(&ctx->options, "--columns");
-    const char *table_name = ctx->argv[1];
-    const struct vtep_ctl_table_class *table;
-
-    table = pre_get_table(ctx, table_name);
-    pre_list_columns(ctx, table, column_names);
-}
-
-static struct table *
-list_make_table(const struct ovsdb_idl_column **columns, size_t n_columns)
-{
-    struct table *out;
-    size_t i;
-
-    out = xmalloc(sizeof *out);
-    table_init(out);
-
-    for (i = 0; i < n_columns; i++) {
-        const struct ovsdb_idl_column *column = columns[i];
-        const char *column_name = column ? column->name : "_uuid";
-
-        table_add_column(out, "%s", column_name);
-    }
-
-    return out;
-}
-
-static void
-list_record(const struct ovsdb_idl_row *row,
-            const struct ovsdb_idl_column **columns, size_t n_columns,
-            struct table *out)
-{
-    size_t i;
-
-    table_add_row(out);
-    for (i = 0; i < n_columns; i++) {
-        const struct ovsdb_idl_column *column = columns[i];
-        struct cell *cell = table_add_cell(out);
-
-        if (!column) {
-            struct ovsdb_datum datum;
-            union ovsdb_atom atom;
-
-            atom.uuid = row->uuid;
-
-            datum.keys = &atom;
-            datum.values = NULL;
-            datum.n = 1;
-
-            cell->json = ovsdb_datum_to_json(&datum, &ovsdb_type_uuid);
-            cell->type = &ovsdb_type_uuid;
-        } else {
-            const struct ovsdb_datum *datum = ovsdb_idl_read(row, column);
-
-            cell->json = ovsdb_datum_to_json(datum, &column->type);
-            cell->type = &column->type;
-        }
-    }
-}
-
-static void
-cmd_list(struct vtep_ctl_context *ctx)
-{
-    const char *column_names = shash_find_data(&ctx->options, "--columns");
-    const struct ovsdb_idl_column **columns;
-    const char *table_name = ctx->argv[1];
-    const struct vtep_ctl_table_class *table;
-    struct table *out;
-    size_t n_columns;
-    int i;
-
-    table = get_table(table_name);
-    parse_column_names(column_names, table, &columns, &n_columns);
-    out = ctx->table = list_make_table(columns, n_columns);
-    if (ctx->argc > 2) {
-        for (i = 2; i < ctx->argc; i++) {
-            list_record(must_get_row(ctx, table, ctx->argv[i]),
-                        columns, n_columns, out);
-        }
-    } else {
-        const struct ovsdb_idl_row *row;
-
-        for (row = ovsdb_idl_first_row(ctx->idl, table->class); row != NULL;
-             row = ovsdb_idl_next_row(row)) {
-            list_record(row, columns, n_columns, out);
-        }
-    }
-    free(columns);
-}
-
-static void
-pre_cmd_find(struct vtep_ctl_context *ctx)
-{
-    const char *column_names = shash_find_data(&ctx->options, "--columns");
-    const char *table_name = ctx->argv[1];
-    const struct vtep_ctl_table_class *table;
-    int i;
-
-    table = pre_get_table(ctx, table_name);
-    pre_list_columns(ctx, table, column_names);
-    for (i = 2; i < ctx->argc; i++) {
-        pre_parse_column_key_value(ctx, ctx->argv[i], table);
-    }
-}
-
-static void
-cmd_find(struct vtep_ctl_context *ctx)
-{
-    const char *column_names = shash_find_data(&ctx->options, "--columns");
-    const struct ovsdb_idl_column **columns;
-    const char *table_name = ctx->argv[1];
-    const struct vtep_ctl_table_class *table;
-    const struct ovsdb_idl_row *row;
-    struct table *out;
-    size_t n_columns;
-
-    table = get_table(table_name);
-    parse_column_names(column_names, table, &columns, &n_columns);
-    out = ctx->table = list_make_table(columns, n_columns);
-    for (row = ovsdb_idl_first_row(ctx->idl, table->class); row;
-         row = ovsdb_idl_next_row(row)) {
-        int i;
-
-        for (i = 2; i < ctx->argc; i++) {
-            if (!is_condition_satisfied(table, row, ctx->argv[i],
-                                        ctx->symtab)) {
-                goto next_row;
-            }
-        }
-        list_record(row, columns, n_columns, out);
-
-    next_row: ;
-    }
-    free(columns);
-}
-
-static void
-pre_cmd_set(struct vtep_ctl_context *ctx)
-{
-    const char *table_name = ctx->argv[1];
-    const struct vtep_ctl_table_class *table;
-    int i;
-
-    table = pre_get_table(ctx, table_name);
-    for (i = 3; i < ctx->argc; i++) {
-        const struct ovsdb_idl_column *column;
-
-        column = pre_parse_column_key_value(ctx, ctx->argv[i], table);
-        check_mutable(table, column);
-    }
-}
-
-static void
-set_column(const struct vtep_ctl_table_class *table,
-           const struct ovsdb_idl_row *row, const char *arg,
-           struct ovsdb_symbol_table *symtab)
-{
-    const struct ovsdb_idl_column *column;
-    char *key_string, *value_string;
-    char *error;
-
-    error = parse_column_key_value(arg, table, &column, &key_string,
-                                   NULL, NULL, 0, &value_string);
-    die_if_error(error);
-    if (!value_string) {
-        vtep_ctl_fatal("%s: missing value", arg);
-    }
-
-    if (key_string) {
-        union ovsdb_atom key, value;
-        struct ovsdb_datum datum;
-
-        if (column->type.value.type == OVSDB_TYPE_VOID) {
-            vtep_ctl_fatal("cannot specify key to set for non-map column %s",
-                        column->name);
-        }
-
-        die_if_error(ovsdb_atom_from_string(&key, &column->type.key,
-                                            key_string, symtab));
-        die_if_error(ovsdb_atom_from_string(&value, &column->type.value,
-                                            value_string, symtab));
-
-        ovsdb_datum_init_empty(&datum);
-        ovsdb_datum_add_unsafe(&datum, &key, &value, &column->type);
-
-        ovsdb_atom_destroy(&key, column->type.key.type);
-        ovsdb_atom_destroy(&value, column->type.value.type);
-
-        ovsdb_datum_union(&datum, ovsdb_idl_read(row, column),
-                          &column->type, false);
-        ovsdb_idl_txn_write(row, column, &datum);
-    } else {
-        struct ovsdb_datum datum;
-
-        die_if_error(ovsdb_datum_from_string(&datum, &column->type,
-                                             value_string, symtab));
-        ovsdb_idl_txn_write(row, column, &datum);
-    }
-
-    free(key_string);
-    free(value_string);
-}
-
-static void
-cmd_set(struct vtep_ctl_context *ctx)
-{
-    const char *table_name = ctx->argv[1];
-    const char *record_id = ctx->argv[2];
-    const struct vtep_ctl_table_class *table;
-    const struct ovsdb_idl_row *row;
-    int i;
-
-    table = get_table(table_name);
-    row = must_get_row(ctx, table, record_id);
-    for (i = 3; i < ctx->argc; i++) {
-        set_column(table, row, ctx->argv[i], ctx->symtab);
-    }
-}
-
-static void
-pre_cmd_add(struct vtep_ctl_context *ctx)
-{
-    const char *table_name = ctx->argv[1];
-    const char *column_name = ctx->argv[3];
-    const struct vtep_ctl_table_class *table;
-    const struct ovsdb_idl_column *column;
-
-    table = pre_get_table(ctx, table_name);
-    pre_get_column(ctx, table, column_name, &column);
-    check_mutable(table, column);
-}
-
-static void
-cmd_add(struct vtep_ctl_context *ctx)
-{
-    const char *table_name = ctx->argv[1];
-    const char *record_id = ctx->argv[2];
-    const char *column_name = ctx->argv[3];
-    const struct vtep_ctl_table_class *table;
-    const struct ovsdb_idl_column *column;
-    const struct ovsdb_idl_row *row;
-    const struct ovsdb_type *type;
-    struct ovsdb_datum old;
-    int i;
-
-    table = get_table(table_name);
-    row = must_get_row(ctx, table, record_id);
-    die_if_error(get_column(table, column_name, &column));
-
-    type = &column->type;
-    ovsdb_datum_clone(&old, ovsdb_idl_read(row, column), &column->type);
-    for (i = 4; i < ctx->argc; i++) {
-        struct ovsdb_type add_type;
-        struct ovsdb_datum add;
-
-        add_type = *type;
-        add_type.n_min = 1;
-        add_type.n_max = UINT_MAX;
-        die_if_error(ovsdb_datum_from_string(&add, &add_type, ctx->argv[i],
-                                             ctx->symtab));
-        ovsdb_datum_union(&old, &add, type, false);
-        ovsdb_datum_destroy(&add, type);
-    }
-    if (old.n > type->n_max) {
-        vtep_ctl_fatal("\"add\" operation would put %u %s in column %s of "
-                    "table %s but the maximum number is %u",
-                    old.n,
-                    type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs",
-                    column->name, table->class->name, type->n_max);
-    }
-    ovsdb_idl_txn_verify(row, column);
-    ovsdb_idl_txn_write(row, column, &old);
-}
-
-static void
-pre_cmd_remove(struct vtep_ctl_context *ctx)
-{
-    const char *table_name = ctx->argv[1];
-    const char *column_name = ctx->argv[3];
-    const struct vtep_ctl_table_class *table;
-    const struct ovsdb_idl_column *column;
-
-    table = pre_get_table(ctx, table_name);
-    pre_get_column(ctx, table, column_name, &column);
-    check_mutable(table, column);
-}
-
-static void
-cmd_remove(struct vtep_ctl_context *ctx)
-{
-    const char *table_name = ctx->argv[1];
-    const char *record_id = ctx->argv[2];
-    const char *column_name = ctx->argv[3];
-    const struct vtep_ctl_table_class *table;
-    const struct ovsdb_idl_column *column;
-    const struct ovsdb_idl_row *row;
-    const struct ovsdb_type *type;
-    struct ovsdb_datum old;
-    int i;
-
-    table = get_table(table_name);
-    row = must_get_row(ctx, table, record_id);
-    die_if_error(get_column(table, column_name, &column));
-
-    type = &column->type;
-    ovsdb_datum_clone(&old, ovsdb_idl_read(row, column), &column->type);
-    for (i = 4; i < ctx->argc; i++) {
-        struct ovsdb_type rm_type;
-        struct ovsdb_datum rm;
-        char *error;
-
-        rm_type = *type;
-        rm_type.n_min = 1;
-        rm_type.n_max = UINT_MAX;
-        error = ovsdb_datum_from_string(&rm, &rm_type,
-                                        ctx->argv[i], ctx->symtab);
-        if (error && ovsdb_type_is_map(&rm_type)) {
-            rm_type.value.type = OVSDB_TYPE_VOID;
-            die_if_error(ovsdb_datum_from_string(&rm, &rm_type,
-                                                 ctx->argv[i], ctx->symtab));
-        }
-        free(error);
-        ovsdb_datum_subtract(&old, type, &rm, &rm_type);
-        ovsdb_datum_destroy(&rm, &rm_type);
-    }
-    if (old.n < type->n_min) {
-        vtep_ctl_fatal("\"remove\" operation would put %u %s in column %s of "
-                    "table %s but the minimum number is %u",
-                    old.n,
-                    type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs",
-                    column->name, table->class->name, type->n_min);
-    }
-    ovsdb_idl_txn_verify(row, column);
-    ovsdb_idl_txn_write(row, column, &old);
-}
-
-static void
-pre_cmd_clear(struct vtep_ctl_context *ctx)
-{
-    const char *table_name = ctx->argv[1];
-    const struct vtep_ctl_table_class *table;
-    int i;
-
-    table = pre_get_table(ctx, table_name);
-    for (i = 3; i < ctx->argc; i++) {
-        const struct ovsdb_idl_column *column;
-
-        pre_get_column(ctx, table, ctx->argv[i], &column);
-        check_mutable(table, column);
-    }
-}
-
-static void
-cmd_clear(struct vtep_ctl_context *ctx)
-{
-    const char *table_name = ctx->argv[1];
-    const char *record_id = ctx->argv[2];
-    const struct vtep_ctl_table_class *table;
-    const struct ovsdb_idl_row *row;
-    int i;
-
-    table = get_table(table_name);
-    row = must_get_row(ctx, table, record_id);
-    for (i = 3; i < ctx->argc; i++) {
-        const struct ovsdb_idl_column *column;
-        const struct ovsdb_type *type;
-        struct ovsdb_datum datum;
-
-        die_if_error(get_column(table, ctx->argv[i], &column));
-
-        type = &column->type;
-        if (type->n_min > 0) {
-            vtep_ctl_fatal("\"clear\" operation cannot be applied to column %s "
-                        "of table %s, which is not allowed to be empty",
-                        column->name, table->class->name);
-        }
-
-        ovsdb_datum_init_empty(&datum);
-        ovsdb_idl_txn_write(row, column, &datum);
-    }
-}
-
-static void
-pre_create(struct vtep_ctl_context *ctx)
-{
-    const char *id = shash_find_data(&ctx->options, "--id");
-    const char *table_name = ctx->argv[1];
-    const struct vtep_ctl_table_class *table;
-
-    table = get_table(table_name);
-    if (!id && !table->class->is_root) {
-        VLOG_WARN("applying \"create\" command to table %s without --id "
-                  "option will have no effect", table->class->name);
-    }
-}
-
-static void
-cmd_create(struct vtep_ctl_context *ctx)
-{
-    const char *id = shash_find_data(&ctx->options, "--id");
-    const char *table_name = ctx->argv[1];
-    const struct vtep_ctl_table_class *table = get_table(table_name);
-    const struct ovsdb_idl_row *row;
-    const struct uuid *uuid;
-    int i;
-
-    if (id) {
-        struct ovsdb_symbol *symbol = create_symbol(ctx->symtab, id, NULL);
-        if (table->class->is_root) {
-            /* This table is in the root set, meaning that rows created in it
-             * won't disappear even if they are unreferenced, so disable
-             * warnings about that by pretending that there is a reference. */
-            symbol->strong_ref = true;
-        }
-        uuid = &symbol->uuid;
-    } else {
-        uuid = NULL;
-    }
-
-    row = ovsdb_idl_txn_insert(ctx->txn, table->class, uuid);
-    for (i = 2; i < ctx->argc; i++) {
-        set_column(table, row, ctx->argv[i], ctx->symtab);
-    }
-    ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(&row->uuid));
-}
-
-/* This function may be used as the 'postprocess' function for commands that
- * insert new rows into the database.  It expects that the command's 'run'
- * function prints the UUID reported by ovsdb_idl_txn_insert() as the command's
- * sole output.  It replaces that output by the row's permanent UUID assigned
- * by the database server and appends a new-line.
- *
- * Currently we use this only for "create", because the higher-level commands
- * are supposed to be independent of the actual structure of the VTEP
- * configuration. */
-static void
-post_create(struct vtep_ctl_context *ctx)
-{
-    const struct uuid *real;
-    struct uuid dummy;
-
-    if (!uuid_from_string(&dummy, ds_cstr(&ctx->output))) {
-        OVS_NOT_REACHED();
-    }
-    real = ovsdb_idl_txn_get_insert_uuid(ctx->txn, &dummy);
-    if (real) {
-        ds_clear(&ctx->output);
-        ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(real));
-    }
-    ds_put_char(&ctx->output, '\n');
-}
-
-static void
-pre_cmd_destroy(struct vtep_ctl_context *ctx)
-{
-    const char *table_name = ctx->argv[1];
-
-    pre_get_table(ctx, table_name);
-}
-
-static void
-cmd_destroy(struct vtep_ctl_context *ctx)
-{
-    bool must_exist = !shash_find(&ctx->options, "--if-exists");
-    bool delete_all = shash_find(&ctx->options, "--all");
-    const char *table_name = ctx->argv[1];
-    const struct vtep_ctl_table_class *table;
-    int i;
-
-    table = get_table(table_name);
-
-    if (delete_all && ctx->argc > 2) {
-        vtep_ctl_fatal("--all and records argument should not be specified together");
-    }
-
-    if (delete_all && !must_exist) {
-        vtep_ctl_fatal("--all and --if-exists should not be specified together");
-    }
-
-    if (delete_all) {
-        const struct ovsdb_idl_row *row;
-        const struct ovsdb_idl_row *next_row;
-
-        for (row = ovsdb_idl_first_row(ctx->idl, table->class);
-             row;) {
-             next_row = ovsdb_idl_next_row(row);
-             ovsdb_idl_txn_delete(row);
-             row = next_row;
-        }
-    } else {
-        for (i = 2; i < ctx->argc; i++) {
-            const struct ovsdb_idl_row *row;
-
-            row = (must_exist ? must_get_row : get_row)(ctx, table, ctx->argv[i]);
-            if (row) {
-                ovsdb_idl_txn_delete(row);
-            }
-        }
-    }
-}
-
-#define RELOPS                                  \
-    RELOP(RELOP_EQ,     "=")                    \
-    RELOP(RELOP_NE,     "!=")                   \
-    RELOP(RELOP_LT,     "<")                    \
-    RELOP(RELOP_GT,     ">")                    \
-    RELOP(RELOP_LE,     "<=")                   \
-    RELOP(RELOP_GE,     ">=")                   \
-    RELOP(RELOP_SET_EQ, "{=}")                  \
-    RELOP(RELOP_SET_NE, "{!=}")                 \
-    RELOP(RELOP_SET_LT, "{<}")                  \
-    RELOP(RELOP_SET_GT, "{>}")                  \
-    RELOP(RELOP_SET_LE, "{<=}")                 \
-    RELOP(RELOP_SET_GE, "{>=}")
-
-enum relop {
-#define RELOP(ENUM, STRING) ENUM,
-    RELOPS
-#undef RELOP
-};
-
-static bool
-is_set_operator(enum relop op)
-{
-    return (op == RELOP_SET_EQ || op == RELOP_SET_NE ||
-            op == RELOP_SET_LT || op == RELOP_SET_GT ||
-            op == RELOP_SET_LE || op == RELOP_SET_GE);
-}
-
-static bool
-evaluate_relop(const struct ovsdb_datum *a, const struct ovsdb_datum *b,
-               const struct ovsdb_type *type, enum relop op)
-{
-    switch (op) {
-    case RELOP_EQ:
-    case RELOP_SET_EQ:
-        return ovsdb_datum_compare_3way(a, b, type) == 0;
-    case RELOP_NE:
-    case RELOP_SET_NE:
-        return ovsdb_datum_compare_3way(a, b, type) != 0;
-    case RELOP_LT:
-        return ovsdb_datum_compare_3way(a, b, type) < 0;
-    case RELOP_GT:
-        return ovsdb_datum_compare_3way(a, b, type) > 0;
-    case RELOP_LE:
-        return ovsdb_datum_compare_3way(a, b, type) <= 0;
-    case RELOP_GE:
-        return ovsdb_datum_compare_3way(a, b, type) >= 0;
-
-    case RELOP_SET_LT:
-        return b->n > a->n && ovsdb_datum_includes_all(a, b, type);
-    case RELOP_SET_GT:
-        return a->n > b->n && ovsdb_datum_includes_all(b, a, type);
-    case RELOP_SET_LE:
-        return ovsdb_datum_includes_all(a, b, type);
-    case RELOP_SET_GE:
-        return ovsdb_datum_includes_all(b, a, type);
-
-    default:
-        OVS_NOT_REACHED();
-    }
-}
-
-static bool
-is_condition_satisfied(const struct vtep_ctl_table_class *table,
-                       const struct ovsdb_idl_row *row, const char *arg,
-                       struct ovsdb_symbol_table *symtab)
-{
-    static const char *operators[] = {
-#define RELOP(ENUM, STRING) STRING,
-        RELOPS
-#undef RELOP
-    };
-
-    const struct ovsdb_idl_column *column;
-    const struct ovsdb_datum *have_datum;
-    char *key_string, *value_string;
-    struct ovsdb_type type;
-    int operator;
-    bool retval;
-    char *error;
-
-    error = parse_column_key_value(arg, table, &column, &key_string,
-                                   &operator, operators, ARRAY_SIZE(operators),
-                                   &value_string);
-    die_if_error(error);
-    if (!value_string) {
-        vtep_ctl_fatal("%s: missing value", arg);
-    }
-
-    type = column->type;
-    type.n_max = UINT_MAX;
-
-    have_datum = ovsdb_idl_read(row, column);
-    if (key_string) {
-        union ovsdb_atom want_key;
-        struct ovsdb_datum b;
-        unsigned int idx;
-
-        if (column->type.value.type == OVSDB_TYPE_VOID) {
-            vtep_ctl_fatal("cannot specify key to check for non-map column %s",
-                        column->name);
-        }
-
-        die_if_error(ovsdb_atom_from_string(&want_key, &column->type.key,
-                                            key_string, symtab));
-
-        type.key = type.value;
-        type.value.type = OVSDB_TYPE_VOID;
-        die_if_error(ovsdb_datum_from_string(&b, &type, value_string, symtab));
-
-        idx = ovsdb_datum_find_key(have_datum,
-                                   &want_key, column->type.key.type);
-        if (idx == UINT_MAX && !is_set_operator(operator)) {
-            retval = false;
-        } else {
-            struct ovsdb_datum a;
-
-            if (idx != UINT_MAX) {
-                a.n = 1;
-                a.keys = &have_datum->values[idx];
-                a.values = NULL;
-            } else {
-                a.n = 0;
-                a.keys = NULL;
-                a.values = NULL;
-            }
-
-            retval = evaluate_relop(&a, &b, &type, operator);
-        }
-
-        ovsdb_atom_destroy(&want_key, column->type.key.type);
-        ovsdb_datum_destroy(&b, &type);
-    } else {
-        struct ovsdb_datum want_datum;
-
-        die_if_error(ovsdb_datum_from_string(&want_datum, &column->type,
-                                             value_string, symtab));
-        retval = evaluate_relop(have_datum, &want_datum, &type, operator);
-        ovsdb_datum_destroy(&want_datum, &column->type);
-    }
-
-    free(key_string);
-    free(value_string);
-
-    return retval;
-}
-
-static void
-pre_cmd_wait_until(struct vtep_ctl_context *ctx)
-{
-    const char *table_name = ctx->argv[1];
-    const struct vtep_ctl_table_class *table;
-    int i;
-
-    table = pre_get_table(ctx, table_name);
-
-    for (i = 3; i < ctx->argc; i++) {
-        pre_parse_column_key_value(ctx, ctx->argv[i], table);
-    }
-}
-
-static void
-cmd_wait_until(struct vtep_ctl_context *ctx)
-{
-    const char *table_name = ctx->argv[1];
-    const char *record_id = ctx->argv[2];
-    const struct vtep_ctl_table_class *table;
-    const struct ovsdb_idl_row *row;
-    int i;
-
-    table = get_table(table_name);
-
-    row = get_row(ctx, table, record_id);
-    if (!row) {
-        ctx->try_again = true;
-        return;
-    }
-
-    for (i = 3; i < ctx->argc; i++) {
-        if (!is_condition_satisfied(table, row, ctx->argv[i], ctx->symtab)) {
-            ctx->try_again = true;
-            return;
-        }
-    }
-}
-\f
-/* Prepares 'ctx', which has already been initialized with
- * vtep_ctl_context_init(), for processing 'command'. */
-static void
-vtep_ctl_context_init_command(struct vtep_ctl_context *ctx,
-                           struct vtep_ctl_command *command)
-{
-    ctx->argc = command->argc;
-    ctx->argv = command->argv;
-    ctx->options = command->options;
-
-    ds_swap(&ctx->output, &command->output);
-    ctx->table = command->table;
-
-    ctx->verified_ports = false;
-
-    ctx->try_again = false;
-}
-
-/* Prepares 'ctx' for processing commands, initializing its members with the
- * values passed in as arguments.
- *
- * If 'command' is nonnull, calls vtep_ctl_context_init_command() to prepare for
- * that particular command. */
-static void
-vtep_ctl_context_init(struct vtep_ctl_context *ctx,
-                      struct vtep_ctl_command *command,
-                      struct ovsdb_idl *idl, struct ovsdb_idl_txn *txn,
-                      const struct vteprec_global *vtep_global,
-                      struct ovsdb_symbol_table *symtab)
-{
-    if (command) {
-        vtep_ctl_context_init_command(ctx, command);
-    }
-    ctx->idl = idl;
-    ctx->txn = txn;
-    ctx->vtep_global = vtep_global;
-    ctx->symtab = symtab;
-    ctx->cache_valid = false;
-}
-
-/* Completes processing of 'command' within 'ctx'. */
-static void
-vtep_ctl_context_done_command(struct vtep_ctl_context *ctx,
-                           struct vtep_ctl_command *command)
-{
-    ds_swap(&ctx->output, &command->output);
-    command->table = ctx->table;
-}
-
-/* Finishes up with 'ctx'.
- *
- * If command is nonnull, first calls vtep_ctl_context_done_command() to complete
- * processing that command within 'ctx'. */
-static void
-vtep_ctl_context_done(struct vtep_ctl_context *ctx, struct vtep_ctl_command *command)
-{
-    if (command) {
-        vtep_ctl_context_done_command(ctx, command);
-    }
-}
-
-static void
-run_prerequisites(struct vtep_ctl_command *commands, size_t n_commands,
-                  struct ovsdb_idl *idl)
-{
-    struct vtep_ctl_command *c;
-
-    ovsdb_idl_add_table(idl, &vteprec_table_global);
-    for (c = commands; c < &commands[n_commands]; c++) {
-        if (c->syntax->prerequisites) {
-            struct vtep_ctl_context ctx;
-
-            ds_init(&c->output);
-            c->table = NULL;
-
-            vtep_ctl_context_init(&ctx, c, idl, NULL, NULL, NULL);
-            (c->syntax->prerequisites)(&ctx);
-            vtep_ctl_context_done(&ctx, c);
-
-            ovs_assert(!c->output.string);
-            ovs_assert(!c->table);
-        }
-    }
-}
-
-static void
-do_vtep_ctl(const char *args, struct vtep_ctl_command *commands,
-            size_t n_commands, struct ovsdb_idl *idl)
-{
-    struct ovsdb_idl_txn *txn;
-    const struct vteprec_global *vtep_global;
-    enum ovsdb_idl_txn_status status;
-    struct ovsdb_symbol_table *symtab;
-    struct vtep_ctl_context ctx;
-    struct vtep_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, "vtep-ctl: %s", args);
-
-    vtep_global = vteprec_global_first(idl);
-    if (!vtep_global) {
-        /* XXX add verification that table is empty */
-        vtep_global = vteprec_global_insert(txn);
-    }
-
-    symtab = ovsdb_symbol_table_create();
-    for (c = commands; c < &commands[n_commands]; c++) {
-        ds_init(&c->output);
-        c->table = NULL;
-    }
-    vtep_ctl_context_init(&ctx, NULL, idl, txn, vtep_global, symtab);
-    for (c = commands; c < &commands[n_commands]; c++) {
-        vtep_ctl_context_init_command(&ctx, c);
-        if (c->syntax->run) {
-            (c->syntax->run)(&ctx);
-        }
-        vtep_ctl_context_done_command(&ctx, c);
-
-        if (ctx.try_again) {
-            vtep_ctl_context_done(&ctx, NULL);
-            goto try_again;
-        }
-    }
-    vtep_ctl_context_done(&ctx, NULL);
-
-    SHASH_FOR_EACH (node, &symtab->sh) {
-        struct ovsdb_symbol *symbol = node->data;
-        if (!symbol->created) {
-            vtep_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) {
-                struct vtep_ctl_context ctx;
-
-                vtep_ctl_context_init(&ctx, c, idl, txn, vtep_global, symtab);
-                (c->syntax->postprocess)(&ctx);
-                vtep_ctl_context_done(&ctx, c);
-            }
-        }
-    }
-    error = xstrdup(ovsdb_idl_txn_get_error(txn));
-    ovsdb_idl_txn_destroy(txn);
-    txn = the_idl_txn = NULL;
-
-    switch (status) {
-    case TXN_UNCOMMITTED:
-    case TXN_INCOMPLETE:
-        OVS_NOT_REACHED();
-
-    case TXN_ABORTED:
-        /* Should not happen--we never call ovsdb_idl_txn_abort(). */
-        vtep_ctl_fatal("transaction aborted");
-
-    case TXN_UNCHANGED:
-    case TXN_SUCCESS:
-        break;
-
-    case TXN_TRY_AGAIN:
-        goto try_again;
-
-    case TXN_ERROR:
-        vtep_ctl_fatal("transaction error: %s", error);
-
-    case TXN_NOT_LOCKED:
-        /* Should not happen--we never call ovsdb_idl_set_lock(). */
-        vtep_ctl_fatal("database not locked");
+    case TXN_NOT_LOCKED:
+        /* Should not happen--we never call ovsdb_idl_set_lock(). */
+        ctl_fatal("database not locked");
 
     default:
         OVS_NOT_REACHED();
@@ -3840,74 +2241,68 @@ try_again:
     free(error);
 }
 
-static const struct vtep_ctl_command_syntax all_commands[] = {
+static const struct ctl_command_syntax vtep_commands[] = {
     /* Physical Switch commands. */
-    {"add-ps", 1, 1, pre_get_info, cmd_add_ps, NULL, "--may-exist", RW},
-    {"del-ps", 1, 1, pre_get_info, cmd_del_ps, NULL, "--if-exists", RW},
-    {"list-ps", 0, 0, pre_get_info, cmd_list_ps, NULL, "", RO},
-    {"ps-exists", 1, 1, pre_get_info, cmd_ps_exists, NULL, "", RO},
+    {"add-ps", 1, 1, NULL, pre_get_info, cmd_add_ps, NULL, "--may-exist", RW},
+    {"del-ps", 1, 1, NULL, pre_get_info, cmd_del_ps, NULL, "--if-exists", RW},
+    {"list-ps", 0, 0, NULL, pre_get_info, cmd_list_ps, NULL, "", RO},
+    {"ps-exists", 1, 1, NULL, pre_get_info, cmd_ps_exists, NULL, "", RO},
 
     /* Port commands. */
-    {"list-ports", 1, 1, pre_get_info, cmd_list_ports, NULL, "", RO},
-    {"add-port", 2, 2, pre_get_info, cmd_add_port, NULL, "--may-exist",
+    {"list-ports", 1, 1, NULL, pre_get_info, cmd_list_ports, NULL, "", RO},
+    {"add-port", 2, 2, NULL, pre_get_info, cmd_add_port, NULL, "--may-exist",
+     RW},
+    {"del-port", 2, 2, NULL, pre_get_info, cmd_del_port, NULL, "--if-exists",
      RW},
-    {"del-port", 2, 2, pre_get_info, cmd_del_port, NULL, "--if-exists", RW},
 
     /* Logical Switch commands. */
-    {"add-ls", 1, 1, pre_get_info, cmd_add_ls, NULL, "--may-exist", RW},
-    {"del-ls", 1, 1, pre_get_info, cmd_del_ls, NULL, "--if-exists", RW},
-    {"list-ls", 0, 0, pre_get_info, cmd_list_ls, NULL, "", RO},
-    {"ls-exists", 1, 1, pre_get_info, cmd_ls_exists, NULL, "", RO},
-    {"list-bindings", 2, 2, pre_get_info, cmd_list_bindings, NULL, "", RO},
-    {"bind-ls", 4, 4, pre_get_info, cmd_bind_ls, NULL, "", RO},
-    {"unbind-ls", 3, 3, pre_get_info, cmd_unbind_ls, NULL, "", RO},
+    {"add-ls", 1, 1, NULL, pre_get_info, cmd_add_ls, NULL, "--may-exist", RW},
+    {"del-ls", 1, 1, NULL, pre_get_info, cmd_del_ls, NULL, "--if-exists", RW},
+    {"list-ls", 0, 0, NULL, pre_get_info, cmd_list_ls, NULL, "", RO},
+    {"ls-exists", 1, 1, NULL, pre_get_info, cmd_ls_exists, NULL, "", RO},
+    {"list-bindings", 2, 2, NULL, pre_get_info, cmd_list_bindings, NULL, "", RO},
+    {"bind-ls", 4, 4, NULL, pre_get_info, cmd_bind_ls, NULL, "", RO},
+    {"unbind-ls", 3, 3, NULL, pre_get_info, cmd_unbind_ls, NULL, "", RO},
 
     /* MAC binding commands. */
-    {"add-ucast-local", 3, 4, pre_get_info, cmd_add_ucast_local, NULL, "", RW},
-    {"del-ucast-local", 2, 2, pre_get_info, cmd_del_ucast_local, NULL, "", RW},
-    {"add-mcast-local", 3, 4, pre_get_info, cmd_add_mcast_local, NULL, "", RW},
-    {"del-mcast-local", 3, 4, pre_get_info, cmd_del_mcast_local, NULL, "", RW},
-    {"clear-local-macs", 1, 1, pre_get_info, cmd_clear_local_macs, NULL, "",
-     RO},
-    {"list-local-macs", 1, 1, pre_get_info, cmd_list_local_macs, NULL, "", RO},
-    {"add-ucast-remote", 3, 4, pre_get_info, cmd_add_ucast_remote, NULL, "",
-     RW},
-    {"del-ucast-remote", 2, 2, pre_get_info, cmd_del_ucast_remote, NULL, "",
-     RW},
-    {"add-mcast-remote", 3, 4, pre_get_info, cmd_add_mcast_remote, NULL, "",
-     RW},
-    {"del-mcast-remote", 3, 4, pre_get_info, cmd_del_mcast_remote, NULL, "",
-     RW},
-    {"clear-remote-macs", 1, 1, pre_get_info, cmd_clear_remote_macs, NULL, "",
-     RO},
-    {"list-remote-macs", 1, 1, pre_get_info, cmd_list_remote_macs, NULL, "",
-     RO},
+    {"add-ucast-local", 3, 4, NULL, pre_get_info, cmd_add_ucast_local, NULL,
+     "", RW},
+    {"del-ucast-local", 2, 2, NULL, pre_get_info, cmd_del_ucast_local, NULL,
+     "", RW},
+    {"add-mcast-local", 3, 4, NULL, pre_get_info, cmd_add_mcast_local, NULL,
+     "", RW},
+    {"del-mcast-local", 3, 4, NULL, pre_get_info, cmd_del_mcast_local, NULL,
+     "", RW},
+    {"clear-local-macs", 1, 1, NULL, pre_get_info, cmd_clear_local_macs, NULL,
+     "", RO},
+    {"list-local-macs", 1, 1, NULL, pre_get_info, cmd_list_local_macs, NULL,
+     "", RO},
+    {"add-ucast-remote", 3, 4, NULL, pre_get_info, cmd_add_ucast_remote, NULL,
+     "", RW},
+    {"del-ucast-remote", 2, 2, NULL, pre_get_info, cmd_del_ucast_remote, NULL,
+     "", RW},
+    {"add-mcast-remote", 3, 4, NULL, pre_get_info, cmd_add_mcast_remote, NULL,
+     "", RW},
+    {"del-mcast-remote", 3, 4, NULL, pre_get_info, cmd_del_mcast_remote, NULL,
+     "", RW},
+    {"clear-remote-macs", 1, 1, NULL, pre_get_info, cmd_clear_remote_macs, NULL,
+     "", RO},
+    {"list-remote-macs", 1, 1, NULL, pre_get_info, cmd_list_remote_macs, NULL,
+     "", RO},
 
     /* Manager commands. */
-    {"get-manager", 0, 0, pre_manager, cmd_get_manager, NULL, "", RO},
-    {"del-manager", 0, 0, pre_manager, cmd_del_manager, NULL, "", RW},
-    {"set-manager", 1, INT_MAX, pre_manager, cmd_set_manager, NULL, "", RW},
-
-    /* Database commands. */
-    {"comment", 0, INT_MAX, NULL, NULL, NULL, "", RO},
-    {"get", 2, INT_MAX, pre_cmd_get, cmd_get, NULL, "--if-exists,--id=", RO},
-    {"list", 1, INT_MAX, pre_cmd_list, cmd_list, NULL, "--columns=", RO},
-    {"find", 1, INT_MAX, pre_cmd_find, cmd_find, NULL, "--columns=", RO},
-    {"set", 3, INT_MAX, pre_cmd_set, cmd_set, NULL, "", RW},
-    {"add", 4, INT_MAX, pre_cmd_add, cmd_add, NULL, "", RW},
-    {"remove", 4, INT_MAX, pre_cmd_remove, cmd_remove, NULL, "", RW},
-    {"clear", 3, INT_MAX, pre_cmd_clear, cmd_clear, NULL, "", RW},
-    {"create", 2, INT_MAX, pre_create, cmd_create, post_create, "--id=", RW},
-    {"destroy", 1, INT_MAX, pre_cmd_destroy, cmd_destroy, NULL,
-     "--if-exists,--all", RW},
-    {"wait-until", 2, INT_MAX, pre_cmd_wait_until, cmd_wait_until, NULL, "",
-     RO},
-
-    {NULL, 0, 0, NULL, NULL, NULL, NULL, RO},
+    {"get-manager", 0, 0, NULL, pre_manager, cmd_get_manager, NULL, "", RO},
+    {"del-manager", 0, 0, NULL, pre_manager, cmd_del_manager, NULL, "", RW},
+    {"set-manager", 1, INT_MAX, NULL, pre_manager, cmd_set_manager, NULL, "",
+     RW},
+
+    {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO},
 };
 
-static const struct vtep_ctl_command_syntax *
-get_all_commands(void)
+/* Registers vsctl and common db commands. */
+static void
+vtep_ctl_cmd_init(void)
 {
-    return all_commands;
+    ctl_init();
+    ctl_register_commands(vtep_commands);
 }