ovs-vsctl: Simplify code.
[cascardo/ovs.git] / utilities / ovs-vsctl.c
index aed90fd..e800f6f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
 #include "compiler.h"
 #include "dirs.h"
 #include "dynamic-string.h"
+#include "fatal-signal.h"
 #include "hash.h"
 #include "json.h"
 #include "ovsdb-data.h"
@@ -46,8 +47,8 @@
 #include "table.h"
 #include "timeval.h"
 #include "util.h"
-#include "vconn.h"
-#include "vlog.h"
+#include "openvswitch/vconn.h"
+#include "openvswitch/vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(vsctl);
 
@@ -62,6 +63,11 @@ struct vsctl_command_syntax {
     int min_args;               /* Min number of arguments following name. */
     int max_args;               /* Max number of arguments following name. */
 
+    /* Names that roughly describe the arguments that the command
+     * uses.  These should be similar to the names displayed in the
+     * man page or in the help output. */
+    const char *arguments;
+
     /* 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 vsctl_context *ctx);
@@ -84,6 +90,7 @@ struct vsctl_command_syntax {
     /* 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? */
 };
 
@@ -128,7 +135,7 @@ static bool retry;
 static struct table_style table_style = TABLE_STYLE_DEFAULT;
 
 /* All supported commands. */
-static const struct vsctl_command_syntax all_commands[];
+static const struct vsctl_command_syntax *get_all_commands(void);
 
 /* The IDL we're using and the current transaction, if any.
  * This is for use by vsctl_exit() only, to allow it to clean up.
@@ -136,10 +143,12 @@ static const struct vsctl_command_syntax all_commands[];
 static struct ovsdb_idl *the_idl;
 static struct ovsdb_idl_txn *the_idl_txn;
 
-static void vsctl_exit(int status) NO_RETURN;
-static void vsctl_fatal(const char *, ...) PRINTF_FORMAT(1, 2) NO_RETURN;
+OVS_NO_RETURN static void vsctl_exit(int status);
+OVS_NO_RETURN static void vsctl_fatal(const char *, ...) OVS_PRINTF_FORMAT(1, 2);
 static char *default_db(void);
-static void usage(void) NO_RETURN;
+OVS_NO_RETURN static void usage(void);
+OVS_NO_RETURN static void print_vsctl_commands(void);
+OVS_NO_RETURN static void print_vsctl_options(const struct option *options);
 static void parse_options(int argc, char *argv[], struct shash *local_options);
 static bool might_write_to_db(char **argv);
 
@@ -164,6 +173,34 @@ static bool is_condition_satisfied(const struct vsctl_table_class *,
                                    const char *arg,
                                    struct ovsdb_symbol_table *);
 
+/* Post_db_reload_check frame work is to allow ovs-vsctl to do additional
+ * checks after OVSDB transactions are successfully recorded and reload by
+ * ovs-vswitchd.
+ *
+ * For example, When a new interface is added to OVSDB, ovs-vswitchd will
+ * either store a positive values on successful implementing the new
+ * interface, or -1 on failure.
+ *
+ * Unless -no-wait command line option is specified,
+ * post_db_reload_do_checks() is called right after any configuration
+ * changes is picked up (i.e. reload) by ovs-vswitchd. Any error detected
+ * post OVSDB reload is reported as ovs-vsctl errors. OVS-vswitchd logs
+ * more detailed messages about those errors.
+ *
+ * Current implementation only check for Post OVSDB reload failures on new
+ * interface additions with 'add-br' and 'add-port' commands.
+ *
+ * post_db_reload_expect_iface()
+ *
+ * keep track of interfaces to be checked post OVSDB reload. */
+static void post_db_reload_check_init(void);
+static void post_db_reload_do_checks(const struct vsctl_context *);
+static void post_db_reload_expect_iface(const struct ovsrec_interface *);
+
+static struct uuid *neoteric_ifaces;
+static size_t n_neoteric_ifaces;
+static size_t allocated_neoteric_ifaces;
+
 int
 main(int argc, char *argv[])
 {
@@ -176,9 +213,9 @@ main(int argc, char *argv[])
     char *args;
 
     set_program_name(argv[0]);
-    signal(SIGPIPE, SIG_IGN);
+    fatal_ignore_sigpipe();
     vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
-    vlog_set_levels(&VLM_reconnect, VLF_ANY_FACILITY, VLL_WARN);
+    vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
     ovsrec_init();
 
     /* Log our arguments.  This is often valuable for debugging systems. */
@@ -263,6 +300,8 @@ parse_options(int argc, char *argv[], struct shash *local_options)
         OPT_PEER_CA_CERT,
         OPT_LOCAL,
         OPT_RETRY,
+        OPT_COMMANDS,
+        OPT_OPTIONS,
         VLOG_OPTION_ENUMS,
         TABLE_OPTION_ENUMS
     };
@@ -275,6 +314,8 @@ parse_options(int argc, char *argv[], struct shash *local_options)
         {"timeout", required_argument, NULL, 't'},
         {"retry", no_argument, NULL, OPT_RETRY},
         {"help", no_argument, NULL, 'h'},
+        {"commands", no_argument, NULL, OPT_COMMANDS},
+        {"options", no_argument, NULL, OPT_OPTIONS},
         {"version", no_argument, NULL, 'V'},
         VLOG_LONG_OPTIONS,
         TABLE_LONG_OPTIONS,
@@ -291,7 +332,7 @@ parse_options(int argc, char *argv[], struct shash *local_options)
     size_t n_options;
     size_t i;
 
-    tmp = long_options_to_short_options(global_long_options);
+    tmp = ovs_cmdl_long_options_to_short_options(global_long_options);
     short_options = xasprintf("+%s", tmp);
     free(tmp);
 
@@ -302,7 +343,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 = all_commands; p->name; p++) {
+    for (p = get_all_commands(); p->name; p++) {
         if (p->options[0]) {
             char *save_ptr = NULL;
             char *name;
@@ -389,8 +430,15 @@ parse_options(int argc, char *argv[], struct shash *local_options)
         case 'h':
             usage();
 
+        case OPT_COMMANDS:
+            print_vsctl_commands();
+
+        case OPT_OPTIONS:
+            print_vsctl_options(global_long_options);
+
         case 'V':
             ovs_print_version(0, 0);
+            printf("DB Schema %s\n", ovsrec_get_db_version());
             exit(EXIT_SUCCESS);
 
         case 't':
@@ -568,7 +616,7 @@ find_command(const char *name)
     if (shash_is_empty(&commands)) {
         const struct vsctl_command_syntax *p;
 
-        for (p = all_commands; p->name; p++) {
+        for (p = get_all_commands(); p->name; p++) {
             shash_add_assert(&commands, p->name, p);
         }
     }
@@ -663,6 +711,11 @@ SSL commands:\n\
   del-ssl                     delete the SSL configuration\n\
   set-ssl PRIV-KEY CERT CA-CERT  set the SSL configuration\n\
 \n\
+Auto Attach commands:\n\
+  add-aa-mapping BRIDGE I-SID VLAN   add Auto Attach mapping to BRIDGE\n\
+  del-aa-mapping BRIDGE I-SID VLAN   delete Auto Attach mapping VLAN from BRIDGE\n\
+  get-aa-mapping BRIDGE              get Auto Attach mappings from BRIDGE\n\
+\n\
 Switch commands:\n\
   emer-reset                  reset switch to known good state\n\
 \n\
@@ -699,6 +752,132 @@ Other options:\n\
     exit(EXIT_SUCCESS);
 }
 
+/* Converts the command arguments into format that can be parsed by
+ * bash completion script.
+ *
+ * Therein, arguments will be attached with following prefixes:
+ *
+ *    !argument :: The argument is required
+ *    ?argument :: The argument is optional
+ *    *argument :: The argument may appear any number (0 or more) times
+ *    +argument :: The argument may appear one or more times
+ *
+ */
+static void
+print_command_arguments(const struct vsctl_command_syntax *command)
+{
+    /*
+     * The argument string is parsed in reverse.  We use a stack 'oew_stack' to
+     * keep track of nested optionals.  Whenever a ']' is encountered, we push
+     * a bit to 'oew_stack'.  The bit is set to 1 if the ']' is not nested.
+     * Subsequently, we pop an entry everytime '[' is met.
+     *
+     * We use 'whole_word_is_optional' value to decide whether or not a ! or +
+     * should be added on encountering a space: if the optional surrounds the
+     * whole word then it shouldn't be, but if it is only a part of the word
+     * (i.e. [key=]value), it should be.
+     */
+    uint32_t oew_stack = 0;
+
+    const char *arguments = command->arguments;
+    int length = strlen(arguments);
+    if (!length) {
+        return;
+    }
+
+    /* Output buffer, written backward from end. */
+    char *output = xmalloc(2 * length);
+    char *outp = output + 2 * length;
+    *--outp = '\0';
+
+    bool in_repeated = false;
+    bool whole_word_is_optional = false;
+
+    for (const char *inp = arguments + length; inp > arguments; ) {
+        switch (*--inp) {
+        case ']':
+            oew_stack <<= 1;
+            if (inp[1] == '\0' || inp[1] == ' ' || inp[1] == '.') {
+                oew_stack |= 1;
+            }
+            break;
+        case '[':
+            /* Checks if the whole word is optional, and sets the
+             * 'whole_word_is_optional' accordingly. */
+            if ((inp == arguments || inp[-1] == ' ') && oew_stack & 1) {
+                *--outp = in_repeated ? '*' : '?';
+                whole_word_is_optional = true;
+            } else {
+                *--outp = '?';
+                whole_word_is_optional = false;
+            }
+            oew_stack >>= 1;
+            break;
+        case ' ':
+            if (!whole_word_is_optional) {
+                *--outp = in_repeated ? '+' : '!';
+            }
+            *--outp = ' ';
+            in_repeated = false;
+            whole_word_is_optional = false;
+            break;
+        case '.':
+            in_repeated = true;
+            break;
+        default:
+            *--outp = *inp;
+            break;
+        }
+    }
+    if (arguments[0] != '[' && outp != output + 2 * length - 1) {
+        *--outp = in_repeated ? '+' : '!';
+    }
+    printf("%s", outp);
+    free(output);
+}
+
+static void
+print_vsctl_commands(void)
+{
+    const struct vsctl_command_syntax *p;
+
+    for (p = get_all_commands(); p->name; p++) {
+        char *options = xstrdup(p->options);
+        char *options_begin = options;
+        char *item;
+
+        for (item = strsep(&options, ","); item != NULL;
+             item = strsep(&options, ",")) {
+            if (item[0] != '\0') {
+                printf("[%s] ", item);
+            }
+        }
+        printf(",%s,", p->name);
+        print_command_arguments(p);
+        printf("\n");
+
+        free(options_begin);
+    }
+
+    exit(EXIT_SUCCESS);
+}
+
+static void
+print_vsctl_options(const struct option *options)
+{
+    for (; options->name; options++) {
+        const struct option *o = options;
+
+        printf("--%s%s\n", o->name, o->has_arg ? "=ARG" : "");
+        if (o->flag == NULL && o->val > 0 && o->val <= UCHAR_MAX) {
+            printf("-%c%s\n", o->val, o->has_arg ? " ARG" : "");
+        }
+    }
+
+    exit(EXIT_SUCCESS);
+}
+
+
 static char *
 default_db(void)
 {
@@ -759,7 +938,7 @@ struct vsctl_context {
 struct vsctl_bridge {
     struct ovsrec_bridge *br_cfg;
     char *name;
-    struct list ports;          /* Contains "struct vsctl_port"s. */
+    struct ovs_list ports;      /* Contains "struct vsctl_port"s. */
 
     /* VLAN ("fake") bridge support.
      *
@@ -772,18 +951,21 @@ struct vsctl_bridge {
 };
 
 struct vsctl_port {
-    struct list ports_node;     /* In struct vsctl_bridge's 'ports' list. */
-    struct list ifaces;         /* Contains "struct vsctl_iface"s. */
+    struct ovs_list ports_node;  /* In struct vsctl_bridge's 'ports' list. */
+    struct ovs_list ifaces;      /* Contains "struct vsctl_iface"s. */
     struct ovsrec_port *port_cfg;
     struct vsctl_bridge *bridge;
 };
 
 struct vsctl_iface {
-    struct list ifaces_node;     /* In struct vsctl_port's 'ifaces' list. */
+    struct ovs_list ifaces_node; /* In struct vsctl_port's 'ifaces' list. */
     struct ovsrec_interface *iface_cfg;
     struct vsctl_port *port;
 };
 
+static struct vsctl_bridge *find_vlan_bridge(struct vsctl_bridge *parent,
+                                             int vlan);
+
 static char *
 vsctl_context_to_string(const struct vsctl_context *ctx)
 {
@@ -840,7 +1022,15 @@ add_bridge_to_cache(struct vsctl_context *ctx,
     br->vlan = vlan;
     hmap_init(&br->children);
     if (parent) {
-        hmap_insert(&parent->children, &br->children_node, hash_int(vlan, 0));
+        struct vsctl_bridge *conflict = find_vlan_bridge(parent, vlan);
+        if (conflict) {
+            VLOG_WARN("%s: bridge has multiple VLAN bridges (%s and %s) "
+                      "for VLAN %d, but only one is allowed",
+                      parent->name, name, conflict->name, vlan);
+        } else {
+            hmap_insert(&parent->children, &br->children_node,
+                        hash_int(vlan, 0));
+        }
     }
     shash_add(&ctx->bridges, br->name, br);
     return br;
@@ -1002,6 +1192,8 @@ pre_get_info(struct vsctl_context *ctx)
     ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_interfaces);
 
     ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_name);
+
+    ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_ofport);
 }
 
 static void
@@ -1295,7 +1487,7 @@ static struct cmd_show_table cmd_show_tables[] = {
      &ovsrec_interface_col_name,
      {&ovsrec_interface_col_type,
       &ovsrec_interface_col_options,
-      NULL},
+      &ovsrec_interface_col_error},
      false},
 
     {&ovsrec_table_controller,
@@ -1453,6 +1645,7 @@ pre_cmd_emer_reset(struct vsctl_context *ctx)
     ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_mirrors);
     ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_netflow);
     ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_sflow);
+    ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_ipfix);
     ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_flood_vlans);
     ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_other_config);
 
@@ -1477,6 +1670,8 @@ cmd_emer_reset(struct vsctl_context *ctx)
     const struct ovsrec_netflow *nf, *next_nf;
     const struct ovsrec_ssl *ssl, *next_ssl;
     const struct ovsrec_sflow *sflow, *next_sflow;
+    const struct ovsrec_ipfix *ipfix, *next_ipfix;
+    const struct ovsrec_flow_sample_collector_set *fscset, *next_fscset;
 
     /* Reset the Open_vSwitch table. */
     ovsrec_open_vswitch_set_manager_options(ctx->ovs, NULL, 0);
@@ -1490,6 +1685,7 @@ cmd_emer_reset(struct vsctl_context *ctx)
         ovsrec_bridge_set_mirrors(br, NULL, 0);
         ovsrec_bridge_set_netflow(br, NULL);
         ovsrec_bridge_set_sflow(br, NULL);
+        ovsrec_bridge_set_ipfix(br, NULL);
         ovsrec_bridge_set_flood_vlans(br, NULL, 0);
 
         /* We only want to save the "hwaddr" key from other_config. */
@@ -1539,6 +1735,14 @@ cmd_emer_reset(struct vsctl_context *ctx)
         ovsrec_sflow_delete(sflow);
     }
 
+    OVSREC_IPFIX_FOR_EACH_SAFE (ipfix, next_ipfix, idl) {
+        ovsrec_ipfix_delete(ipfix);
+    }
+
+    OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH_SAFE (fscset, next_fscset, idl) {
+        ovsrec_flow_sample_collector_set_delete(fscset);
+    }
+
     vsctl_context_invalidate_cache(ctx);
 }
 
@@ -1547,6 +1751,7 @@ cmd_add_br(struct vsctl_context *ctx)
 {
     bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
     const char *br_name, *parent_name;
+    struct ovsrec_interface *iface;
     int vlan;
 
     br_name = ctx->argv[1];
@@ -1600,7 +1805,6 @@ cmd_add_br(struct vsctl_context *ctx)
 
     if (!parent_name) {
         struct ovsrec_port *port;
-        struct ovsrec_interface *iface;
         struct ovsrec_bridge *br;
 
         iface = ovsrec_interface_insert(ctx->txn);
@@ -1617,9 +1821,9 @@ cmd_add_br(struct vsctl_context *ctx)
 
         ovs_insert_bridge(ctx->ovs, br);
     } else {
+        struct vsctl_bridge *conflict;
         struct vsctl_bridge *parent;
         struct ovsrec_port *port;
-        struct ovsrec_interface *iface;
         struct ovsrec_bridge *br;
         int64_t tag = vlan;
 
@@ -1630,6 +1834,11 @@ cmd_add_br(struct vsctl_context *ctx)
         if (!parent) {
             vsctl_fatal("parent bridge %s does not exist", parent_name);
         }
+        conflict = find_vlan_bridge(parent, vlan);
+        if (conflict) {
+            vsctl_fatal("bridge %s already has a child VLAN bridge %s "
+                        "on VLAN %d", parent_name, conflict->name, vlan);
+        }
         br = parent->br_cfg;
 
         iface = ovsrec_interface_insert(ctx->txn);
@@ -1645,6 +1854,7 @@ cmd_add_br(struct vsctl_context *ctx)
         bridge_insert_port(br, port);
     }
 
+    post_db_reload_expect_iface(iface);
     vsctl_context_invalidate_cache(ctx);
 }
 
@@ -1668,6 +1878,7 @@ del_bridge(struct vsctl_context *ctx, struct vsctl_bridge *br)
 {
     struct vsctl_bridge *child, *next_child;
     struct vsctl_port *port, *next_port;
+    const struct ovsrec_flow_sample_collector_set *fscset, *next_fscset;
 
     HMAP_FOR_EACH_SAFE (child, next_child, children_node, &br->children) {
         del_bridge(ctx, child);
@@ -1677,6 +1888,13 @@ del_bridge(struct vsctl_context *ctx, struct vsctl_bridge *br)
         del_port(ctx, port);
     }
 
+    OVSREC_FLOW_SAMPLE_COLLECTOR_SET_FOR_EACH_SAFE (fscset, next_fscset,
+                                                    ctx->idl) {
+        if (fscset->bridge == br->br_cfg) {
+            ovsrec_flow_sample_collector_set_delete(fscset);
+        }
+    }
+
     del_cached_bridge(ctx, br);
 }
 
@@ -1930,6 +2148,7 @@ add_port(struct vsctl_context *ctx,
     for (i = 0; i < n_ifaces; i++) {
         ifaces[i] = ovsrec_interface_insert(ctx->txn);
         ovsrec_interface_set_name(ifaces[i], iface_names[i]);
+        post_db_reload_expect_iface(ifaces[i]);
     }
 
     port = ovsrec_port_insert(ctx->txn);
@@ -1996,13 +2215,20 @@ cmd_del_port(struct vsctl_context *ctx)
 {
     bool must_exist = !shash_find(&ctx->options, "--if-exists");
     bool with_iface = shash_find(&ctx->options, "--with-iface") != NULL;
+    const char *target = ctx->argv[ctx->argc - 1];
     struct vsctl_port *port;
 
     vsctl_context_populate_cache(ctx);
-    if (!with_iface) {
-        port = find_port(ctx, ctx->argv[ctx->argc - 1], must_exist);
+    if (find_bridge(ctx, target, false)) {
+        if (must_exist) {
+            vsctl_fatal("cannot delete port %s because it is the local port "
+                        "for bridge %s (deleting this port requires deleting "
+                        "the entire bridge)", target, target);
+        }
+        port = NULL;
+    } else if (!with_iface) {
+        port = find_port(ctx, target, must_exist);
     } else {
-        const char *target = ctx->argv[ctx->argc - 1];
         struct vsctl_iface *iface;
 
         port = find_port(ctx, target, false);
@@ -2318,9 +2544,8 @@ cmd_get_manager(struct vsctl_context *ctx)
 }
 
 static void
-delete_managers(const struct vsctl_context *ctx)
+delete_managers(const struct ovsrec_open_vswitch *ovs)
 {
-    const struct ovsrec_open_vswitch *ovs = ctx->ovs;
     size_t i;
 
     /* Delete Manager rows pointed to by 'manager_options' column. */
@@ -2338,7 +2563,7 @@ cmd_del_manager(struct vsctl_context *ctx)
     const struct ovsrec_open_vswitch *ovs = ctx->ovs;
 
     verify_managers(ovs);
-    delete_managers(ctx);
+    delete_managers(ovs);
 }
 
 static void
@@ -2368,7 +2593,7 @@ cmd_set_manager(struct vsctl_context *ctx)
     const size_t n = ctx->argc - 1;
 
     verify_managers(ctx->ovs);
-    delete_managers(ctx);
+    delete_managers(ctx->ovs);
     insert_managers(ctx, &ctx->argv[1], n);
 }
 
@@ -2447,6 +2672,180 @@ cmd_set_ssl(struct vsctl_context *ctx)
 
     ovsrec_open_vswitch_set_ssl(ctx->ovs, ssl);
 }
+
+static void
+autoattach_insert_mapping(struct ovsrec_autoattach *aa,
+                          int64_t isid,
+                          int64_t vlan)
+{
+    int64_t *key_mappings, *value_mappings;
+    size_t i;
+
+    key_mappings = xmalloc(sizeof *aa->key_mappings * (aa->n_mappings + 1));
+    value_mappings = xmalloc(sizeof *aa->value_mappings * (aa->n_mappings + 1));
+
+    for (i = 0; i < aa->n_mappings; i++) {
+        key_mappings[i] = aa->key_mappings[i];
+        value_mappings[i] = aa->value_mappings[i];
+    }
+    key_mappings[aa->n_mappings] = isid;
+    value_mappings[aa->n_mappings] = vlan;
+
+    ovsrec_autoattach_set_mappings(aa, key_mappings, value_mappings,
+                                   aa->n_mappings + 1);
+
+    free(key_mappings);
+    free(value_mappings);
+}
+
+static void
+cmd_add_aa_mapping(struct vsctl_context *ctx)
+{
+    struct vsctl_bridge *br;
+    int64_t isid, vlan;
+    char *nptr = NULL;
+
+    isid = strtoull(ctx->argv[2], &nptr, 10);
+    if (nptr == ctx->argv[2] || nptr == NULL) {
+        vsctl_fatal("Invalid argument %s", ctx->argv[2]);
+        return;
+    }
+
+    vlan = strtoull(ctx->argv[3], &nptr, 10);
+    if (nptr == ctx->argv[3] || nptr == NULL) {
+        vsctl_fatal("Invalid argument %s", ctx->argv[3]);
+        return;
+    }
+
+    vsctl_context_populate_cache(ctx);
+
+    br = find_bridge(ctx, ctx->argv[1], true);
+    if (br->parent) {
+        br = br->parent;
+    }
+
+    if (br && br->br_cfg) {
+        if (!br->br_cfg->auto_attach) {
+            struct ovsrec_autoattach *aa = ovsrec_autoattach_insert(ctx->txn);
+            ovsrec_bridge_set_auto_attach(br->br_cfg, aa);
+        }
+        autoattach_insert_mapping(br->br_cfg->auto_attach, isid, vlan);
+    }
+}
+
+static void
+del_aa_mapping(struct ovsrec_autoattach *aa,
+               int64_t isid,
+               int64_t vlan)
+{
+    int64_t *key_mappings, *value_mappings;
+    size_t i, n;
+
+    key_mappings = xmalloc(sizeof *aa->key_mappings * (aa->n_mappings));
+    value_mappings = xmalloc(sizeof *value_mappings * (aa->n_mappings));
+
+    for (i = n = 0; i < aa->n_mappings; i++) {
+        if (aa->key_mappings[i] != isid && aa->value_mappings[i] != vlan) {
+            key_mappings[n] = aa->key_mappings[i];
+            value_mappings[n++] = aa->value_mappings[i];
+        }
+    }
+
+    ovsrec_autoattach_set_mappings(aa, key_mappings, value_mappings, n);
+
+    free(key_mappings);
+    free(value_mappings);
+}
+
+static void
+cmd_del_aa_mapping(struct vsctl_context *ctx)
+{
+    struct vsctl_bridge *br;
+    int64_t isid, vlan;
+    char *nptr = NULL;
+
+    isid = strtoull(ctx->argv[2], &nptr, 10);
+    if (nptr == ctx->argv[2] || nptr == NULL) {
+        vsctl_fatal("Invalid argument %s", ctx->argv[2]);
+        return;
+    }
+
+    vlan = strtoull(ctx->argv[3], &nptr, 10);
+    if (nptr == ctx->argv[3] || nptr == NULL) {
+        vsctl_fatal("Invalid argument %s", ctx->argv[3]);
+        return;
+    }
+
+    vsctl_context_populate_cache(ctx);
+
+    br = find_bridge(ctx, ctx->argv[1], true);
+    if (br->parent) {
+        br = br->parent;
+    }
+
+    if (br && br->br_cfg && br->br_cfg->auto_attach &&
+        br->br_cfg->auto_attach->key_mappings &&
+        br->br_cfg->auto_attach->value_mappings) {
+        size_t i;
+
+        for (i = 0; i < br->br_cfg->auto_attach->n_mappings; i++) {
+            if (br->br_cfg->auto_attach->key_mappings[i] == isid &&
+                br->br_cfg->auto_attach->value_mappings[i] == vlan) {
+                del_aa_mapping(br->br_cfg->auto_attach, isid, vlan);
+                break;
+            }
+        }
+    }
+}
+
+static void
+pre_aa_mapping(struct vsctl_context *ctx)
+{
+    pre_get_info(ctx);
+
+    ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_auto_attach);
+    ovsdb_idl_add_column(ctx->idl, &ovsrec_autoattach_col_mappings);
+}
+
+static void
+verify_auto_attach(struct ovsrec_bridge *bridge)
+{
+    if (bridge) {
+        ovsrec_bridge_verify_auto_attach(bridge);
+
+        if (bridge->auto_attach) {
+            ovsrec_autoattach_verify_mappings(bridge->auto_attach);
+        }
+    }
+}
+
+static void
+cmd_get_aa_mapping(struct vsctl_context *ctx)
+{
+    struct vsctl_bridge *br;
+
+    vsctl_context_populate_cache(ctx);
+
+    br = find_bridge(ctx, ctx->argv[1], true);
+    if (br->parent) {
+        br = br->parent;
+    }
+
+    verify_auto_attach(br->br_cfg);
+
+    if (br && br->br_cfg && br->br_cfg->auto_attach &&
+        br->br_cfg->auto_attach->key_mappings &&
+        br->br_cfg->auto_attach->value_mappings) {
+        size_t i;
+
+        for (i = 0; i < br->br_cfg->auto_attach->n_mappings; i++) {
+            ds_put_format(&ctx->output, "%"PRId64" %"PRId64"\n",
+                          br->br_cfg->auto_attach->key_mappings[i],
+                          br->br_cfg->auto_attach->value_mappings[i]);
+        }
+    }
+}
+
 \f
 /* Parameter commands. */
 
@@ -2464,7 +2863,8 @@ struct vsctl_table_class {
 static const struct vsctl_table_class tables[] = {
     {&ovsrec_table_bridge,
      {{&ovsrec_table_bridge, &ovsrec_bridge_col_name, NULL},
-      {NULL, NULL, NULL}}},
+      {&ovsrec_table_flow_sample_collector_set, NULL,
+       &ovsrec_flow_sample_collector_set_col_bridge}}},
 
     {&ovsrec_table_controller,
      {{&ovsrec_table_bridge,
@@ -2518,6 +2918,23 @@ static const struct vsctl_table_class tables[] = {
      {{&ovsrec_table_flow_table, &ovsrec_flow_table_col_name, NULL},
       {NULL, NULL, NULL}}},
 
+    {&ovsrec_table_ipfix,
+     {{&ovsrec_table_bridge,
+       &ovsrec_bridge_col_name,
+       &ovsrec_bridge_col_ipfix},
+      {&ovsrec_table_flow_sample_collector_set, NULL,
+       &ovsrec_flow_sample_collector_set_col_ipfix}}},
+
+    {&ovsrec_table_autoattach,
+     {{&ovsrec_table_bridge,
+       &ovsrec_bridge_col_name,
+       &ovsrec_bridge_col_auto_attach},
+      {NULL, NULL, NULL}}},
+
+    {&ovsrec_table_flow_sample_collector_set,
+     {{NULL, NULL, NULL},
+      {NULL, NULL, NULL}}},
+
     {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
 };
 
@@ -2673,9 +3090,11 @@ get_row (struct vsctl_context *ctx,
     const struct ovsdb_idl_row *row;
     struct uuid uuid;
 
+    row = NULL;
     if (uuid_from_string(&uuid, record_id)) {
         row = ovsdb_idl_get_row_for_uuid(ctx->idl, table->class, &uuid);
-    } else {
+    }
+    if (!row) {
         int i;
 
         for (i = 0; i < ARRAY_SIZE(table->row_ids); i++) {
@@ -2796,7 +3215,7 @@ missing_operator_error(const char *arg, const char **allowed_operators,
  *
  * 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 * WARN_UNUSED_RESULT
+static char * OVS_WARN_UNUSED_RESULT
 parse_column_key_value(const char *arg,
                        const struct vsctl_table_class *table,
                        const struct ovsdb_idl_column **columnp, char **keyp,
@@ -2916,12 +3335,12 @@ pre_parse_column_key_value(struct vsctl_context *ctx,
 }
 
 static void
-check_mutable(const struct vsctl_table_class *table,
+check_mutable(const struct ovsdb_idl_row *row,
               const struct ovsdb_idl_column *column)
 {
-    if (!column->mutable) {
+    if (!ovsdb_idl_is_mutable(row, column)) {
         vsctl_fatal("cannot modify read-only column %s in table %s",
-                    column->name, table->class->name);
+                    column->name, row->table->class->name);
     }
 }
 
@@ -3268,10 +3687,7 @@ pre_cmd_set(struct vsctl_context *ctx)
 
     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);
+        pre_parse_column_key_value(ctx, ctx->argv[i], table);
     }
 }
 
@@ -3290,6 +3706,7 @@ set_column(const struct vsctl_table_class *table,
     if (!value_string) {
         vsctl_fatal("%s: missing value", arg);
     }
+    check_mutable(row, column);
 
     if (key_string) {
         union ovsdb_atom key, value;
@@ -3313,6 +3730,7 @@ set_column(const struct vsctl_table_class *table,
 
         ovsdb_datum_union(&datum, ovsdb_idl_read(row, column),
                           &column->type, false);
+        ovsdb_idl_txn_verify(row, column);
         ovsdb_idl_txn_write(row, column, &datum);
     } else {
         struct ovsdb_datum datum;
@@ -3359,7 +3777,6 @@ pre_cmd_add(struct vsctl_context *ctx)
 
     table = pre_get_table(ctx, table_name);
     pre_get_column(ctx, table, column_name, &column);
-    check_mutable(table, column);
 }
 
 static void
@@ -3382,6 +3799,7 @@ cmd_add(struct vsctl_context *ctx)
     if (!row) {
         return;
     }
+    check_mutable(row, column);
 
     type = &column->type;
     ovsdb_datum_clone(&old, ovsdb_idl_read(row, column), &column->type);
@@ -3420,7 +3838,6 @@ pre_cmd_remove(struct vsctl_context *ctx)
 
     table = pre_get_table(ctx, table_name);
     pre_get_column(ctx, table, column_name, &column);
-    check_mutable(table, column);
 }
 
 static void
@@ -3443,6 +3860,7 @@ cmd_remove(struct vsctl_context *ctx)
     if (!row) {
         return;
     }
+    check_mutable(row, column);
 
     type = &column->type;
     ovsdb_datum_clone(&old, ovsdb_idl_read(row, column), &column->type);
@@ -3456,11 +3874,16 @@ cmd_remove(struct vsctl_context *ctx)
         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)) {
-            free(error);
-            rm_type.value.type = OVSDB_TYPE_VOID;
-            die_if_error(ovsdb_datum_from_string(&rm, &rm_type,
-                                                 ctx->argv[i], ctx->symtab));
+
+        if (error) {
+            if (ovsdb_type_is_map(&rm_type)) {
+                rm_type.value.type = OVSDB_TYPE_VOID;
+                free(error);
+                die_if_error(ovsdb_datum_from_string(
+                                 &rm, &rm_type, ctx->argv[i], ctx->symtab));
+            } else {
+                vsctl_fatal("%s", error);
+            }
         }
         ovsdb_datum_subtract(&old, type, &rm, &rm_type);
         ovsdb_datum_destroy(&rm, &rm_type);
@@ -3490,7 +3913,6 @@ pre_cmd_clear(struct vsctl_context *ctx)
         const struct ovsdb_idl_column *column;
 
         pre_get_column(ctx, table, ctx->argv[i], &column);
-        check_mutable(table, column);
     }
 }
 
@@ -3516,6 +3938,7 @@ cmd_clear(struct vsctl_context *ctx)
         struct ovsdb_datum datum;
 
         die_if_error(get_column(table, ctx->argv[i], &column));
+        check_mutable(row, column);
 
         type = &column->type;
         if (type->n_min > 0) {
@@ -3591,7 +4014,7 @@ post_create(struct vsctl_context *ctx)
     struct uuid dummy;
 
     if (!uuid_from_string(&dummy, ds_cstr(&ctx->output))) {
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
     real = ovsdb_idl_txn_get_insert_uuid(ctx->txn, &dummy);
     if (real) {
@@ -3601,6 +4024,52 @@ post_create(struct vsctl_context *ctx)
     ds_put_char(&ctx->output, '\n');
 }
 
+static void
+post_db_reload_check_init(void)
+{
+    n_neoteric_ifaces = 0;
+}
+
+static void
+post_db_reload_expect_iface(const struct ovsrec_interface *iface)
+{
+    if (n_neoteric_ifaces >= allocated_neoteric_ifaces) {
+        neoteric_ifaces = x2nrealloc(neoteric_ifaces,
+                                     &allocated_neoteric_ifaces,
+                                     sizeof *neoteric_ifaces);
+    }
+    neoteric_ifaces[n_neoteric_ifaces++] = iface->header_.uuid;
+}
+
+static void
+post_db_reload_do_checks(const struct vsctl_context *ctx)
+{
+    struct ds dead_ifaces = DS_EMPTY_INITIALIZER;
+    size_t i;
+
+    for (i = 0; i < n_neoteric_ifaces; i++) {
+        const struct uuid *uuid;
+
+        uuid = ovsdb_idl_txn_get_insert_uuid(ctx->txn, &neoteric_ifaces[i]);
+        if (uuid) {
+            const struct ovsrec_interface *iface;
+
+            iface = ovsrec_interface_get_for_uuid(ctx->idl, uuid);
+            if (iface && (!iface->ofport || *iface->ofport == -1)) {
+                ds_put_format(&dead_ifaces, "'%s', ", iface->name);
+            }
+        }
+    }
+
+    if (dead_ifaces.length) {
+        dead_ifaces.length -= 2; /* Strip off trailing comma and space. */
+        ovs_error(0, "Error detected while setting up %s.  See ovs-vswitchd "
+                  "log for details.", ds_cstr(&dead_ifaces));
+    }
+
+    ds_destroy(&dead_ifaces);
+}
+
 static void
 pre_cmd_destroy(struct vsctl_context *ctx)
 {
@@ -3709,7 +4178,7 @@ evaluate_relop(const struct ovsdb_datum *a, const struct ovsdb_datum *b,
         return ovsdb_datum_includes_all(b, a, type);
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
 }
 
@@ -3957,6 +4426,7 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
                                 &ovsrec_open_vswitch_col_next_cfg);
     }
 
+    post_db_reload_check_init();
     symtab = ovsdb_symbol_table_create();
     for (c = commands; c < &commands[n_commands]; c++) {
         ds_init(&c->output);
@@ -4013,13 +4483,11 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
         }
     }
     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:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
 
     case TXN_ABORTED:
         /* Should not happen--we never call ovsdb_idl_txn_abort(). */
@@ -4040,7 +4508,7 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
         vsctl_fatal("database not locked");
 
     default:
-        NOT_REACHED();
+        OVS_NOT_REACHED();
     }
     free(error);
 
@@ -4083,10 +4551,16 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
     free(commands);
 
     if (wait_for_reload && status != TXN_UNCHANGED) {
+        /* Even, if --retry flag was not specified, ovs-vsctl still
+         * has to retry to establish OVSDB connection, if wait_for_reload
+         * was set.  Otherwise, ovs-vsctl would end up waiting forever
+         * until cur_cfg would be updated. */
+        ovsdb_idl_enable_reconnect(idl);
         for (;;) {
             ovsdb_idl_run(idl);
             OVSREC_OPEN_VSWITCH_FOR_EACH (ovs, idl) {
                 if (ovs->cur_cfg >= next_cfg) {
+                    post_db_reload_do_checks(&ctx);
                     goto done;
                 }
             }
@@ -4095,6 +4569,7 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
         }
     done: ;
     }
+    ovsdb_idl_txn_destroy(txn);
     ovsdb_idl_destroy(idl);
 
     exit(EXIT_SUCCESS);
@@ -4116,76 +4591,128 @@ try_again:
     free(error);
 }
 
+/*
+ * Developers who add new commands to the 'struct vsctl_command_syntax' must
+ * define the 'arguments' member of the struct.  The following keywords are
+ * available for composing the argument format:
+ *
+ *    TABLE     RECORD       BRIDGE       PARENT         PORT
+ *    KEY       VALUE        ARG          KEY=VALUE      ?KEY=VALUE
+ *    IFACE     SYSIFACE     COLUMN       COLUMN?:KEY    COLUMN?:KEY=VALUE
+ *    MODE      CA-CERT      CERTIFICATE  PRIVATE-KEY
+ *    TARGET    NEW-* (e.g. NEW-PORT)
+ *
+ * For argument types not listed above, just uses 'ARG' as place holder.
+ *
+ * Encloses the keyword with '[]' if it is optional.  Appends '...' to
+ * keyword or enclosed keyword to indicate that the argument can be specified
+ * multiple times.
+ *
+ * */
 static const struct vsctl_command_syntax all_commands[] = {
     /* Open vSwitch commands. */
-    {"init", 0, 0, NULL, cmd_init, NULL, "", RW},
-    {"show", 0, 0, pre_cmd_show, cmd_show, NULL, "", RO},
+    {"init", 0, 0, "", NULL, cmd_init, NULL, "", RW},
+    {"show", 0, 0, "", pre_cmd_show, cmd_show, NULL, "", RO},
 
     /* Bridge commands. */
-    {"add-br", 1, 3, pre_get_info, cmd_add_br, NULL, "--may-exist", RW},
-    {"del-br", 1, 1, pre_get_info, cmd_del_br, NULL, "--if-exists", RW},
-    {"list-br", 0, 0, pre_get_info, cmd_list_br, NULL, "--real,--fake", RO},
-    {"br-exists", 1, 1, pre_get_info, cmd_br_exists, NULL, "", RO},
-    {"br-to-vlan", 1, 1, pre_get_info, cmd_br_to_vlan, NULL, "", RO},
-    {"br-to-parent", 1, 1, pre_get_info, cmd_br_to_parent, NULL, "", RO},
-    {"br-set-external-id", 2, 3, pre_cmd_br_set_external_id,
-     cmd_br_set_external_id, NULL, "", RW},
-    {"br-get-external-id", 1, 2, pre_cmd_br_get_external_id,
+    {"add-br", 1, 3, "NEW-BRIDGE [PARENT] [NEW-VLAN]", pre_get_info,
+     cmd_add_br, NULL, "--may-exist", RW},
+    {"del-br", 1, 1, "BRIDGE", pre_get_info, cmd_del_br,
+     NULL, "--if-exists", RW},
+    {"list-br", 0, 0, "", pre_get_info, cmd_list_br, NULL, "--real,--fake",
+     RO},
+    {"br-exists", 1, 1, "BRIDGE", pre_get_info, cmd_br_exists, NULL, "", RO},
+    {"br-to-vlan", 1, 1, "BRIDGE", pre_get_info, cmd_br_to_vlan, NULL, "",
+     RO},
+    {"br-to-parent", 1, 1, "BRIDGE", pre_get_info, cmd_br_to_parent, NULL,
+     "", RO},
+    {"br-set-external-id", 2, 3, "BRIDGE KEY [VALUE]",
+     pre_cmd_br_set_external_id, cmd_br_set_external_id, NULL, "", RW},
+    {"br-get-external-id", 1, 2, "BRIDGE [KEY]", pre_cmd_br_get_external_id,
      cmd_br_get_external_id, NULL, "", RO},
 
     /* Port commands. */
-    {"list-ports", 1, 1, pre_get_info, cmd_list_ports, NULL, "", RO},
-    {"add-port", 2, INT_MAX, pre_get_info, cmd_add_port, NULL, "--may-exist",
-     RW},
-    {"add-bond", 4, INT_MAX, pre_get_info, cmd_add_bond, NULL,
-     "--may-exist,--fake-iface", RW},
-    {"del-port", 1, 2, pre_get_info, cmd_del_port, NULL,
+    {"list-ports", 1, 1, "BRIDGE", pre_get_info, cmd_list_ports, NULL, "",
+     RO},
+    {"add-port", 2, INT_MAX, "BRIDGE NEW-PORT [COLUMN[:KEY]=VALUE]...",
+     pre_get_info, cmd_add_port, NULL, "--may-exist", RW},
+    {"add-bond", 4, INT_MAX,
+     "BRIDGE NEW-BOND-PORT SYSIFACE... [COLUMN[:KEY]=VALUE]...", pre_get_info,
+     cmd_add_bond, NULL, "--may-exist,--fake-iface", RW},
+    {"del-port", 1, 2, "[BRIDGE] PORT|IFACE", pre_get_info, cmd_del_port, NULL,
      "--if-exists,--with-iface", RW},
-    {"port-to-br", 1, 1, pre_get_info, cmd_port_to_br, NULL, "", RO},
+    {"port-to-br", 1, 1, "PORT", pre_get_info, cmd_port_to_br, NULL, "", RO},
 
     /* Interface commands. */
-    {"list-ifaces", 1, 1, pre_get_info, cmd_list_ifaces, NULL, "", RO},
-    {"iface-to-br", 1, 1, pre_get_info, cmd_iface_to_br, NULL, "", RO},
+    {"list-ifaces", 1, 1, "BRIDGE", pre_get_info, cmd_list_ifaces, NULL, "",
+     RO},
+    {"iface-to-br", 1, 1, "IFACE", pre_get_info, cmd_iface_to_br, NULL, "",
+     RO},
 
     /* Controller commands. */
-    {"get-controller", 1, 1, pre_controller, cmd_get_controller, NULL, "", RO},
-    {"del-controller", 1, 1, pre_controller, cmd_del_controller, NULL, "", RW},
-    {"set-controller", 1, INT_MAX, pre_controller, cmd_set_controller, NULL,
+    {"get-controller", 1, 1, "BRIDGE", pre_controller, cmd_get_controller,
+     NULL, "", RO},
+    {"del-controller", 1, 1, "BRIDGE", pre_controller, cmd_del_controller,
+     NULL, "", RW},
+    {"set-controller", 1, INT_MAX, "BRIDGE TARGET...", pre_controller,
+     cmd_set_controller, NULL, "", RW},
+    {"get-fail-mode", 1, 1, "BRIDGE", pre_get_info, cmd_get_fail_mode, NULL,
+     "", RO},
+    {"del-fail-mode", 1, 1, "BRIDGE", pre_get_info, cmd_del_fail_mode, NULL,
      "", RW},
-    {"get-fail-mode", 1, 1, pre_get_info, cmd_get_fail_mode, NULL, "", RO},
-    {"del-fail-mode", 1, 1, pre_get_info, cmd_del_fail_mode, NULL, "", RW},
-    {"set-fail-mode", 2, 2, pre_get_info, cmd_set_fail_mode, NULL, "", RW},
+    {"set-fail-mode", 2, 2, "BRIDGE MODE", pre_get_info, cmd_set_fail_mode,
+     NULL, "", RW},
 
     /* 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},
+    {"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, "TARGET...", pre_manager, cmd_set_manager,
+     NULL, "", RW},
 
     /* SSL commands. */
-    {"get-ssl", 0, 0, pre_cmd_get_ssl, cmd_get_ssl, NULL, "", RO},
-    {"del-ssl", 0, 0, pre_cmd_del_ssl, cmd_del_ssl, NULL, "", RW},
-    {"set-ssl", 3, 3, pre_cmd_set_ssl, cmd_set_ssl, NULL, "--bootstrap", RW},
+    {"get-ssl", 0, 0, "", pre_cmd_get_ssl, cmd_get_ssl, NULL, "", RO},
+    {"del-ssl", 0, 0, "", pre_cmd_del_ssl, cmd_del_ssl, NULL, "", RW},
+    {"set-ssl", 3, 3, "PRIVATE-KEY CERTIFICATE CA-CERT", pre_cmd_set_ssl,
+     cmd_set_ssl, NULL, "--bootstrap", RW},
+
+    /* Auto Attach commands. */
+    {"add-aa-mapping", 3, 3, "BRIDGE ARG ARG", pre_aa_mapping, cmd_add_aa_mapping,
+     NULL, "", RW},
+    {"del-aa-mapping", 3, 3, "BRIDGE ARG ARG", pre_aa_mapping, cmd_del_aa_mapping,
+     NULL, "", RW},
+    {"get-aa-mapping", 1, 1, "BRIDGE", pre_aa_mapping, cmd_get_aa_mapping,
+     NULL, "", RO},
 
     /* Switch commands. */
-    {"emer-reset", 0, 0, pre_cmd_emer_reset, cmd_emer_reset, NULL, "", RW},
+    {"emer-reset", 0, 0, "", pre_cmd_emer_reset, cmd_emer_reset, 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,
+    {"comment", 0, INT_MAX, "[ARG]...", NULL, NULL, NULL, "", RO},
+    {"get", 2, INT_MAX, "TABLE RECORD [COLUMN[:KEY]]...",pre_cmd_get, cmd_get,
+     NULL, "--if-exists,--id=", RO},
+    {"list", 1, INT_MAX, "TABLE [RECORD]...", pre_cmd_list, cmd_list, NULL,
      "--if-exists,--columns=", RO},
-    {"find", 1, INT_MAX, pre_cmd_find, cmd_find, NULL, "--columns=", RO},
-    {"set", 3, INT_MAX, pre_cmd_set, cmd_set, NULL, "--if-exists", RW},
-    {"add", 4, INT_MAX, pre_cmd_add, cmd_add, NULL, "--if-exists", RW},
-    {"remove", 4, INT_MAX, pre_cmd_remove, cmd_remove, NULL, "--if-exists",
-     RW},
-    {"clear", 3, INT_MAX, pre_cmd_clear, cmd_clear, NULL, "--if-exists", 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},
+    {"find", 1, INT_MAX, "TABLE [COLUMN[:KEY]=VALUE]...", pre_cmd_find,
+     cmd_find, NULL, "--columns=", RO},
+    {"set", 3, INT_MAX, "TABLE RECORD COLUMN[:KEY]=VALUE...", pre_cmd_set,
+     cmd_set, NULL, "--if-exists", RW},
+    {"add", 4, INT_MAX, "TABLE RECORD COLUMN [KEY=]VALUE...", pre_cmd_add,
+     cmd_add, NULL, "--if-exists", RW},
+    {"remove", 4, INT_MAX, "TABLE RECORD COLUMN KEY|VALUE|KEY=VALUE...",
+     pre_cmd_remove, cmd_remove, NULL, "--if-exists", RW},
+    {"clear", 3, INT_MAX, "TABLE RECORD COLUMN...", pre_cmd_clear, cmd_clear,
+     NULL, "--if-exists", RW},
+    {"create", 2, INT_MAX, "TABLE COLUMN[:KEY]=VALUE...", pre_create,
+     cmd_create, post_create, "--id=", RW},
+    {"destroy", 1, INT_MAX, "TABLE [RECORD]...", pre_cmd_destroy, cmd_destroy,
+     NULL, "--if-exists,--all", RW},
+    {"wait-until", 2, INT_MAX, "TABLE RECORD [COLUMN[:KEY]=VALUE]...",
+     pre_cmd_wait_until, cmd_wait_until, NULL, "", RO},
+
+    {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO},
 };
 
+static const struct vsctl_command_syntax *get_all_commands(void)
+{
+    return all_commands;
+}