auto-attach: Add auto-attach support to bridge layer and command set
[cascardo/ovs.git] / utilities / ovs-vsctl.c
index 37d8b3c..00e48d5 100644 (file)
@@ -47,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);
 
@@ -137,10 +137,10 @@ static const struct vsctl_command_syntax *get_all_commands(void);
 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);
 static void parse_options(int argc, char *argv[], struct shash *local_options);
 static bool might_write_to_db(char **argv);
 
@@ -207,7 +207,7 @@ main(int argc, char *argv[])
     set_program_name(argv[0]);
     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. */
@@ -693,6 +693,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\
@@ -789,7 +794,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.
      *
@@ -802,18 +807,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)
 {
@@ -870,7 +878,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;
@@ -1025,6 +1041,7 @@ pre_get_info(struct vsctl_context *ctx)
     ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_controller);
     ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_fail_mode);
     ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_ports);
+    ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_auto_attach);
 
     ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_name);
     ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_fake_bridge);
@@ -1032,6 +1049,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_autoattach_col_mappings);
     ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_ofport);
 }
 
@@ -1644,6 +1663,7 @@ cmd_add_br(struct vsctl_context *ctx)
 
     if (!parent_name) {
         struct ovsrec_port *port;
+        struct ovsrec_autoattach *aa;
         struct ovsrec_bridge *br;
 
         iface = ovsrec_interface_insert(ctx->txn);
@@ -1654,12 +1674,16 @@ cmd_add_br(struct vsctl_context *ctx)
         ovsrec_port_set_name(port, br_name);
         ovsrec_port_set_interfaces(port, &iface, 1);
 
+        aa = ovsrec_autoattach_insert(ctx->txn);
+
         br = ovsrec_bridge_insert(ctx->txn);
         ovsrec_bridge_set_name(br, br_name);
         ovsrec_bridge_set_ports(br, &port, 1);
+        ovsrec_bridge_set_auto_attach(br, aa);
 
         ovs_insert_bridge(ctx->ovs, br);
     } else {
+        struct vsctl_bridge *conflict;
         struct vsctl_bridge *parent;
         struct ovsrec_port *port;
         struct ovsrec_bridge *br;
@@ -1672,6 +1696,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);
@@ -2506,6 +2535,175 @@ 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) {
+        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_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",
+                          (long int) br->br_cfg->auto_attach->key_mappings[i],
+                          (long int) br->br_cfg->auto_attach->value_mappings[i]);
+        }
+    }
+}
+
 \f
 /* Parameter commands. */
 
@@ -2585,6 +2783,12 @@ static const struct vsctl_table_class tables[] = {
       {&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}}},
@@ -2744,9 +2948,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++) {
@@ -2867,7 +3073,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,
@@ -2987,12 +3193,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);
     }
 }
 
@@ -3339,10 +3545,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);
     }
 }
 
@@ -3361,6 +3564,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;
@@ -3431,7 +3635,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
@@ -3454,6 +3657,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);
@@ -3492,7 +3696,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
@@ -3515,6 +3718,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);
@@ -3528,11 +3732,15 @@ 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);
@@ -3562,7 +3770,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);
     }
 }
 
@@ -3588,6 +3795,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) {
@@ -4290,6 +4498,11 @@ static const struct vsctl_command_syntax all_commands[] = {
     {"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},
 
+    /* Auto Attach commands. */
+    {"add-aa-mapping", 3, 3, pre_get_info, cmd_add_aa_mapping, NULL, "", RW},
+    {"del-aa-mapping", 3, 3, pre_aa_mapping, cmd_del_aa_mapping, NULL, "", RW},
+    {"get-aa-mapping", 1, 1, pre_aa_mapping, cmd_get_aa_mapping, NULL, "", RO},
+
     /* Switch commands. */
     {"emer-reset", 0, 0, pre_cmd_emer_reset, cmd_emer_reset, NULL, "", RW},