dpif-netlink: add GENEVE creation support
[cascardo/ovs.git] / lib / db-ctl-base.c
index d1dc8f7..9f50c6c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Nicira, Inc.
+ * Copyright (c) 2015, 2016 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@
 #include "command-line.h"
 #include "compiler.h"
 #include "dirs.h"
-#include "dynamic-string.h"
+#include "openvswitch/dynamic-string.h"
 #include "fatal-signal.h"
 #include "hash.h"
 #include "json.h"
 #include "ovsdb-idl.h"
 #include "ovsdb-idl-provider.h"
 #include "shash.h"
+#include "sset.h"
 #include "string.h"
 #include "table.h"
 #include "util.h"
 
 VLOG_DEFINE_THIS_MODULE(db_ctl_base);
 
-/* The IDL we're using and the current transaction, if any.
- * This is for use by ctl_exit() only, to allow it to clean up.
- * Other code should use its context arguments. */
-struct ovsdb_idl *the_idl;
-struct ovsdb_idl_txn *the_idl_txn;
+/* This array defines the 'show' command output format.  User can check the
+ * definition in utilities/ovs-vsctl.c as reference.
+ *
+ * Particularly, if an element in 'columns[]' represents a reference to
+ * another table, the referred table must also be defined as an entry in
+ * in 'cmd_show_tables[]'.
+ *
+ * The definition must end with an all-NULL entry.  It is initalized once
+ * when ctl_init() is called.
+ *
+ * */
+static const struct cmd_show_table *cmd_show_tables;
+
+/* ctl_exit() is called by ctl_fatal(). User can optionally supply an exit
+ * function ctl_exit_func() via ctl_init. If supplied, this function will
+ * be called by ctl_exit()
+ */
+static void (*ctl_exit_func)(int status) = NULL;
+OVS_NO_RETURN static void ctl_exit(int status);
+
+/* Represents all tables in the schema.  User must define 'tables'
+ * in implementation and supply via ctl_init().  The definition must end
+ * with an all-NULL entry. */
+static const struct ctl_table_class *tables;
 
 static struct shash all_commands = SHASH_INITIALIZER(&all_commands);
+static const struct ctl_table_class *get_table(const char *table_name);
+static void set_column(const struct ctl_table_class *,
+                       const struct ovsdb_idl_row *, const char *,
+                       struct ovsdb_symbol_table *);
 
 \f
 static struct option *
@@ -230,18 +254,30 @@ get_row_by_id(struct ctl_context *ctx, const struct ctl_table_class *table,
             return NULL;
         }
     } 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)) {
+        ovs_assert(id->name_column->type.value.type == OVSDB_TYPE_VOID);
+
+        enum ovsdb_atomic_type key = id->name_column->type.key.type;
+        if (key == OVSDB_TYPE_INTEGER) {
+            if (!record_id[0] || record_id[strspn(record_id, "0123456789")]) {
+                return NULL;
+            }
+        } else {
+            ovs_assert(key == OVSDB_TYPE_STRING);
+        }
+
+        for (const struct ovsdb_idl_row *row = ovsdb_idl_first_row(ctx->idl,
+                                                                   id->table);
+             row != NULL;
+             row = ovsdb_idl_next_row(row)) {
+            const struct ovsdb_datum *name = ovsdb_idl_get(
+                row, id->name_column, key, OVSDB_TYPE_VOID);
+            if (name->n == 1) {
+                const union ovsdb_atom *atom = &name->keys[0];
+                if (key == OVSDB_TYPE_STRING
+                    ? !strcmp(atom->string, record_id)
+                    : atom->integer == strtoll(record_id, NULL, 10)) {
                     if (referrer) {
                         ctl_fatal("multiple rows in %s match \"%s\"",
                                   table->class->name, record_id);
@@ -249,6 +285,7 @@ get_row_by_id(struct ctl_context *ctx, const struct ctl_table_class *table,
                     referrer = row;
                 }
             }
+        }
     }
     if (!referrer) {
         return NULL;
@@ -979,6 +1016,35 @@ cmd_list(struct ctl_context *ctx)
     free(columns);
 }
 
+/* Finds and returns the "struct ctl_table_class *" with 'table_name' by
+ * searching the 'tables'. */
+static const struct ctl_table_class *
+get_table(const char *table_name)
+{
+    const struct ctl_table_class *table;
+    const struct ctl_table_class *best_match = NULL;
+    unsigned int best_score = 0;
+
+    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;
+        }
+    }
+    if (best_match) {
+        return best_match;
+    } else if (best_score) {
+        ctl_fatal("multiple table names match \"%s\"", table_name);
+    } else {
+        ctl_fatal("unknown table \"%s\"", table_name);
+    }
+    return NULL;
+}
+
 static void
 pre_cmd_find(struct ctl_context *ctx)
 {
@@ -1025,6 +1091,60 @@ cmd_find(struct ctl_context *ctx)
     free(columns);
 }
 
+/* Sets the column of 'row' in 'table'. */
+static void
+set_column(const struct 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) {
+        ctl_fatal("%s: missing value", arg);
+    }
+    check_mutable(row, column);
+
+    if (key_string) {
+        union ovsdb_atom key, value;
+        struct ovsdb_datum datum;
+
+        if (column->type.value.type == OVSDB_TYPE_VOID) {
+            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_verify(row, column);
+        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
 pre_cmd_set(struct ctl_context *ctx)
 {
@@ -1044,7 +1164,7 @@ cmd_set(struct ctl_context *ctx)
     bool must_exist = !shash_find(&ctx->options, "--if-exists");
     const char *table_name = ctx->argv[1];
     const char *record_id = ctx->argv[2];
-    const struct ctl_table_class*table;
+    const struct ctl_table_class *table;
     const struct ovsdb_idl_row *row;
     int i;
 
@@ -1495,6 +1615,218 @@ parse_command(int argc, char *argv[], struct shash *local_options,
     command->argv = &argv[i];
 }
 
+static void
+pre_cmd_show(struct ctl_context *ctx)
+{
+    const struct cmd_show_table *show;
+
+    for (show = cmd_show_tables; show->table; show++) {
+        size_t i;
+
+        ovsdb_idl_add_table(ctx->idl, show->table);
+        if (show->name_column) {
+            ovsdb_idl_add_column(ctx->idl, show->name_column);
+        }
+        for (i = 0; i < ARRAY_SIZE(show->columns); i++) {
+            const struct ovsdb_idl_column *column = show->columns[i];
+            if (column) {
+                ovsdb_idl_add_column(ctx->idl, column);
+            }
+        }
+        if (show->wref_table.table) {
+            ovsdb_idl_add_table(ctx->idl, show->wref_table.table);
+        }
+        if (show->wref_table.name_column) {
+            ovsdb_idl_add_column(ctx->idl, show->wref_table.name_column);
+        }
+        if (show->wref_table.wref_column) {
+            ovsdb_idl_add_column(ctx->idl, show->wref_table.wref_column);
+        }
+    }
+}
+
+static const struct cmd_show_table *
+cmd_show_find_table_by_row(const struct ovsdb_idl_row *row)
+{
+    const struct cmd_show_table *show;
+
+    for (show = cmd_show_tables; show->table; show++) {
+        if (show->table == row->table->class) {
+            return show;
+        }
+    }
+    return NULL;
+}
+
+static const struct cmd_show_table *
+cmd_show_find_table_by_name(const char *name)
+{
+    const struct cmd_show_table *show;
+
+    for (show = cmd_show_tables; show->table; show++) {
+        if (!strcmp(show->table->name, name)) {
+            return show;
+        }
+    }
+    return NULL;
+}
+
+/*  Prints table entries that weak reference the 'cur_row'. */
+static void
+cmd_show_weak_ref(struct ctl_context *ctx, const struct cmd_show_table *show,
+                  const struct ovsdb_idl_row *cur_row, int level)
+{
+    const struct ovsdb_idl_row *row_wref;
+    const struct ovsdb_idl_table_class *table = show->wref_table.table;
+    const struct ovsdb_idl_column *name_column
+        = show->wref_table.name_column;
+    const struct ovsdb_idl_column *wref_column
+        = show->wref_table.wref_column;
+
+    if (!table || !name_column || !wref_column) {
+        return;
+    }
+
+    for (row_wref = ovsdb_idl_first_row(ctx->idl, table); row_wref;
+         row_wref = ovsdb_idl_next_row(row_wref)) {
+        const struct ovsdb_datum *wref_datum
+            = ovsdb_idl_read(row_wref, wref_column);
+        /* If weak reference refers to the 'cur_row', prints it. */
+        if (wref_datum->n
+            && uuid_equals(&cur_row->uuid, &wref_datum->keys[0].uuid)) {
+            const struct ovsdb_datum *name_datum
+                = ovsdb_idl_read(row_wref, name_column);
+            ds_put_char_multiple(&ctx->output, ' ', (level + 1) * 4);
+            ds_put_format(&ctx->output, "%s ", table->name);
+            ovsdb_datum_to_string(name_datum, &name_column->type, &ctx->output);
+            ds_put_char(&ctx->output, '\n');
+        }
+    }
+}
+
+/* 'shown' records the tables that has been displayed by the current
+ * command to avoid duplicated prints.
+ */
+static void
+cmd_show_row(struct ctl_context *ctx, const struct ovsdb_idl_row *row,
+             int level, struct sset *shown)
+{
+    const struct cmd_show_table *show = cmd_show_find_table_by_row(row);
+    size_t i;
+
+    ds_put_char_multiple(&ctx->output, ' ', level * 4);
+    if (show && show->name_column) {
+        const struct ovsdb_datum *datum;
+
+        ds_put_format(&ctx->output, "%s ", show->table->name);
+        datum = ovsdb_idl_read(row, show->name_column);
+        ovsdb_datum_to_string(datum, &show->name_column->type, &ctx->output);
+    } else {
+        ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(&row->uuid));
+    }
+    ds_put_char(&ctx->output, '\n');
+
+    if (!show || sset_find(shown, show->table->name)) {
+        return;
+    }
+
+    sset_add(shown, show->table->name);
+    for (i = 0; i < ARRAY_SIZE(show->columns); i++) {
+        const struct ovsdb_idl_column *column = show->columns[i];
+        const struct ovsdb_datum *datum;
+
+        if (!column) {
+            break;
+        }
+
+        datum = ovsdb_idl_read(row, column);
+        if (column->type.key.type == OVSDB_TYPE_UUID &&
+            column->type.key.u.uuid.refTableName) {
+            const struct cmd_show_table *ref_show;
+            size_t j;
+
+            ref_show = cmd_show_find_table_by_name(
+                column->type.key.u.uuid.refTableName);
+            if (ref_show) {
+                for (j = 0; j < datum->n; j++) {
+                    const struct ovsdb_idl_row *ref_row;
+
+                    ref_row = ovsdb_idl_get_row_for_uuid(ctx->idl,
+                                                         ref_show->table,
+                                                         &datum->keys[j].uuid);
+                    if (ref_row) {
+                        cmd_show_row(ctx, ref_row, level + 1, shown);
+                    }
+                }
+                continue;
+            }
+        } else if (ovsdb_type_is_map(&column->type) &&
+                   column->type.value.type == OVSDB_TYPE_UUID &&
+                   column->type.value.u.uuid.refTableName) {
+            const struct cmd_show_table *ref_show;
+            size_t j;
+
+            /* Prints the key to ref'ed table name map if the ref'ed table
+             * is also defined in 'cmd_show_tables'.  */
+            ref_show = cmd_show_find_table_by_name(
+                column->type.value.u.uuid.refTableName);
+            if (ref_show && ref_show->name_column) {
+                ds_put_char_multiple(&ctx->output, ' ', (level + 1) * 4);
+                ds_put_format(&ctx->output, "%s:\n", column->name);
+                for (j = 0; j < datum->n; j++) {
+                    const struct ovsdb_idl_row *ref_row;
+
+                    ref_row = ovsdb_idl_get_row_for_uuid(ctx->idl,
+                                                         ref_show->table,
+                                                         &datum->values[j].uuid);
+
+                    ds_put_char_multiple(&ctx->output, ' ', (level + 2) * 4);
+                    ovsdb_atom_to_string(&datum->keys[j], column->type.key.type,
+                                         &ctx->output);
+                    ds_put_char(&ctx->output, '=');
+                    if (ref_row) {
+                        const struct ovsdb_datum *ref_datum;
+
+                        ref_datum = ovsdb_idl_read(ref_row,
+                                                   ref_show->name_column);
+                        ovsdb_datum_to_string(ref_datum,
+                                              &ref_show->name_column->type,
+                                              &ctx->output);
+                    } else {
+                        ds_put_cstr(&ctx->output, "\"<null>\"");
+                    }
+                    ds_put_char(&ctx->output, '\n');
+                }
+                continue;
+            }
+        }
+
+        if (!ovsdb_datum_is_default(datum, &column->type)) {
+            ds_put_char_multiple(&ctx->output, ' ', (level + 1) * 4);
+            ds_put_format(&ctx->output, "%s: ", column->name);
+            ovsdb_datum_to_string(datum, &column->type, &ctx->output);
+            ds_put_char(&ctx->output, '\n');
+        }
+    }
+    cmd_show_weak_ref(ctx, show, row, level);
+    sset_find_and_delete_assert(shown, show->table->name);
+}
+
+static void
+cmd_show(struct ctl_context *ctx)
+{
+    const struct ovsdb_idl_row *row;
+    struct sset shown = SSET_INITIALIZER(&shown);
+
+    for (row = ovsdb_idl_first_row(ctx->idl, cmd_show_tables[0].table);
+         row; row = ovsdb_idl_next_row(row)) {
+        cmd_show_row(ctx, row, 0, &shown);
+    }
+
+    ovs_assert(sset_is_empty(&shown));
+    sset_destroy(&shown);
+}
+
 \f
 /* Given pointer to dynamic array 'options_p',  array's current size
  * 'allocated_options_p' and number of added options 'n_options_p',
@@ -1508,7 +1840,7 @@ ctl_add_cmd_options(struct option **options_p, size_t *n_options_p,
     const struct shash_node *node;
     size_t n_existing_options = *n_options_p;
 
-    SHASH_FOR_EACH (node, ctl_get_all_commands()) {
+    SHASH_FOR_EACH (node, &all_commands) {
         const struct ctl_command_syntax *p = node->data;
 
         if (p->options[0]) {
@@ -1598,7 +1930,7 @@ ctl_print_commands(void)
 {
     const struct shash_node *node;
 
-    SHASH_FOR_EACH (node, ctl_get_all_commands()) {
+    SHASH_FOR_EACH (node, &all_commands) {
         const struct ctl_command_syntax *p = node->data;
         char *options = xstrdup(p->options);
         char *options_begin = options;
@@ -1654,7 +1986,8 @@ bool
 ctl_might_write_to_db(char **argv)
 {
     for (; *argv; argv++) {
-        const struct ctl_command_syntax *p = shash_find_data(&all_commands, *argv);
+        const struct ctl_command_syntax *p = shash_find_data(&all_commands,
+                                                             *argv);
         if (p && p->mode == RW) {
             return true;
         }
@@ -1672,7 +2005,7 @@ ctl_fatal(const char *format, ...)
     message = xvasprintf(format, args);
     va_end(args);
 
-    vlog_set_levels(&VLM_db_ctl_base, VLF_CONSOLE, VLL_OFF);
+    vlog_set_levels(&this_module, VLF_CONSOLE, VLL_OFF);
     VLOG_ERR("%s", message);
     ovs_error(0, "%s", message);
     ctl_exit(EXIT_FAILURE);
@@ -1684,14 +2017,12 @@ ctl_fatal(const char *format, ...)
  * 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. */
-void
+static void
 ctl_exit(int status)
 {
-    if (the_idl_txn) {
-        ovsdb_idl_txn_abort(the_idl_txn);
-        ovsdb_idl_txn_destroy(the_idl_txn);
+    if (ctl_exit_func) {
+        ctl_exit_func(status);
     }
-    ovsdb_idl_destroy(the_idl);
     exit(status);
 }
 
@@ -1721,6 +2052,12 @@ static const struct ctl_command_syntax db_ctl_commands[] = {
     {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, RO},
 };
 
+static void
+ctl_register_command(const struct ctl_command_syntax *command)
+{
+    shash_add_assert(&all_commands, command->name, command);
+}
+
 /* Registers commands represented by 'struct ctl_command_syntax's to
  * 'all_commands'.  The last element of 'commands' must be an all-NULL
  * element. */
@@ -1730,22 +2067,26 @@ ctl_register_commands(const struct ctl_command_syntax *commands)
     const struct ctl_command_syntax *p;
 
     for (p = commands; p->name; p++) {
-        shash_add_assert(&all_commands, p->name, p);
+        ctl_register_command(p);
     }
 }
 
 /* Registers the 'db_ctl_commands' to 'all_commands'. */
 void
-ctl_init(void)
+ctl_init(const struct ctl_table_class tables_[],
+         const struct cmd_show_table cmd_show_tables_[],
+         void (*ctl_exit_func_)(int status))
 {
+    tables = tables_;
+    ctl_exit_func = ctl_exit_func_;
     ctl_register_commands(db_ctl_commands);
-}
 
-/* Returns 'all_commands'. */
-const struct shash *
-ctl_get_all_commands(void)
-{
-    return &all_commands;
+    cmd_show_tables = cmd_show_tables_;
+    if (cmd_show_tables) {
+        static const struct ctl_command_syntax show =
+            {"show", 0, 0, "", pre_cmd_show, cmd_show, NULL, "", RO};
+        ctl_register_command(&show);
+    }
 }
 
 /* Returns the text for the database commands usage.  */
@@ -1819,85 +2160,9 @@ ctl_context_done(struct ctl_context *ctx,
     invalidate_cache(ctx);
 }
 
-/* Finds and returns the "struct ctl_table_class *" with 'table_name' by
- * searching the 'tables'. */
-const struct ctl_table_class *
-get_table(const char *table_name)
-{
-    const struct ctl_table_class *table;
-    const struct ctl_table_class *best_match = NULL;
-    unsigned int best_score = 0;
-
-    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;
-        }
-    }
-    if (best_match) {
-        return best_match;
-    } else if (best_score) {
-        ctl_fatal("multiple table names match \"%s\"", table_name);
-    } else {
-        ctl_fatal("unknown table \"%s\"", table_name);
-    }
-    return NULL;
-}
-
-/* Sets the column of 'row' in 'table'. */
-void
-set_column(const struct ctl_table_class *table,
-           const struct ovsdb_idl_row *row, const char *arg,
-           struct ovsdb_symbol_table *symtab)
+void ctl_set_column(const char *table_name,
+                    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) {
-        ctl_fatal("%s: missing value", arg);
-    }
-    check_mutable(row, column);
-
-    if (key_string) {
-        union ovsdb_atom key, value;
-        struct ovsdb_datum datum;
-
-        if (column->type.value.type == OVSDB_TYPE_VOID) {
-            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_verify(row, column);
-        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);
+    set_column(get_table(table_name), row, arg, symtab);
 }