ovsdb: Implement garbage collection.
authorBen Pfaff <blp@nicira.com>
Thu, 10 Mar 2011 19:15:01 +0000 (11:15 -0800)
committerBen Pfaff <blp@nicira.com>
Thu, 10 Mar 2011 19:24:00 +0000 (11:24 -0800)
27 files changed:
debian/openvswitch-switch.init
lib/ovsdb-data.c
lib/ovsdb-data.h
lib/ovsdb-idl-provider.h
ovsdb/SPECS
ovsdb/dot2pic
ovsdb/ovsdb-doc.in
ovsdb/ovsdb-dot.in
ovsdb/ovsdb-idlc.in
ovsdb/ovsdb.c
ovsdb/table.c
ovsdb/table.h
ovsdb/transaction.c
python/ovs/db/schema.py
tests/ovs-vsctl.at
tests/ovsdb-execution.at
tests/ovsdb-table.at
tests/test-ovsdb.c
tests/test-ovsdb.py
utilities/ovs-vsctl.8.in
utilities/ovs-vsctl.c
vswitchd/ovs-brcompatd.c
vswitchd/vswitch.gv
vswitchd/vswitch.ovsschema
vswitchd/vswitch.pic
vswitchd/vswitch.xml
xenserver/etc_init.d_openvswitch

index 92ab775..8ea5866 100755 (executable)
@@ -232,6 +232,18 @@ case "$1" in
             cksum=`ovsdb-tool db-cksum "$conf_file" | awk '{print $1}'`
             cp "$conf_file" "$conf_file.backup$version-$cksum"
             
+            # Compact database.  This is important if the old schema did not
+            # enable garbage collection (i.e. if it did not have any tables
+            # with "isRoot": true) but the new schema does.  In that situation
+            # the old database may contain a transaction that creates a record
+            # followed by a transaction that creates the first use of the
+            # record.  Replaying that series of transactions against the new
+            # database schema (as "convert" does) would cause the record to be
+            # dropped by the first transaction, then the second transaction
+            # would cause a referential integrity failure (for a strong
+            # reference).
+            ovsdb-tool -vANY:console:emer compact $conf_file
+
             # Upgrade or downgrade schema and compact database.
             ovsdb-tool -vANY:console:emer convert $conf_file $schema_file
         fi
index ac94864..150ae61 100644 (file)
@@ -285,9 +285,28 @@ parse_json_pair(const struct json *json,
     return NULL;
 }
 
+static void
+ovsdb_symbol_referenced(struct ovsdb_symbol *symbol,
+                        const struct ovsdb_base_type *base)
+{
+    assert(base->type == OVSDB_TYPE_UUID);
+
+    if (base->u.uuid.refTableName) {
+        switch (base->u.uuid.refType) {
+        case OVSDB_REF_STRONG:
+            symbol->strong_ref = true;
+            break;
+        case OVSDB_REF_WEAK:
+            symbol->weak_ref = true;
+            break;
+        }
+    }
+}
+
 static struct ovsdb_error * WARN_UNUSED_RESULT
 ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,
-                      struct ovsdb_symbol_table *symtab)
+                      struct ovsdb_symbol_table *symtab,
+                      const struct ovsdb_base_type *base)
 {
     struct ovsdb_error *error0;
     const struct json *value;
@@ -304,14 +323,17 @@ ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,
 
         error1 = unwrap_json(json, "named-uuid", JSON_STRING, &value);
         if (!error1) {
-            const char *name = json_string(value);
+            struct ovsdb_symbol *symbol;
 
             ovsdb_error_destroy(error0);
-            *uuid = ovsdb_symbol_table_insert(symtab, name)->uuid;
             if (!ovsdb_parser_is_id(json_string(value))) {
                 return ovsdb_syntax_error(json, NULL, "named-uuid string is "
                                           "not a valid <id>");
             }
+
+            symbol = ovsdb_symbol_table_insert(symtab, json_string(value));
+            *uuid = symbol->uuid;
+            ovsdb_symbol_referenced(symbol, base);
             return NULL;
         }
         ovsdb_error_destroy(error1);
@@ -321,10 +343,13 @@ ovsdb_atom_parse_uuid(struct uuid *uuid, const struct json *json,
 }
 
 static struct ovsdb_error * WARN_UNUSED_RESULT
-ovsdb_atom_from_json__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
+ovsdb_atom_from_json__(union ovsdb_atom *atom,
+                       const struct ovsdb_base_type *base,
                        const struct json *json,
                        struct ovsdb_symbol_table *symtab)
 {
+    enum ovsdb_atomic_type type = base->type;
+
     switch (type) {
     case OVSDB_TYPE_VOID:
         NOT_REACHED();
@@ -364,7 +389,7 @@ ovsdb_atom_from_json__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
         break;
 
     case OVSDB_TYPE_UUID:
-        return ovsdb_atom_parse_uuid(&atom->uuid, json, symtab);
+        return ovsdb_atom_parse_uuid(&atom->uuid, json, symtab, base);
 
     case OVSDB_N_TYPES:
     default:
@@ -384,7 +409,9 @@ ovsdb_atom_from_json__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
  *
  * If 'symtab' is nonnull, then named UUIDs in 'symtab' are accepted.  Refer to
  * ovsdb/SPECS for information about this, and for the syntax that this
- * function accepts. */
+ * function accepts.  If 'base' is a reference and a symbol is parsed, then the
+ * symbol's 'strong_ref' or 'weak_ref' member is set to true, as
+ * appropriate. */
 struct ovsdb_error *
 ovsdb_atom_from_json(union ovsdb_atom *atom,
                      const struct ovsdb_base_type *base,
@@ -393,7 +420,7 @@ ovsdb_atom_from_json(union ovsdb_atom *atom,
 {
     struct ovsdb_error *error;
 
-    error = ovsdb_atom_from_json__(atom, base->type, json, symtab);
+    error = ovsdb_atom_from_json__(atom, base, json, symtab);
     if (error) {
         return error;
     }
@@ -440,9 +467,12 @@ ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type)
 }
 
 static char *
-ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
-                         const char *s, struct ovsdb_symbol_table *symtab)
+ovsdb_atom_from_string__(union ovsdb_atom *atom,
+                         const struct ovsdb_base_type *base, const char *s,
+                         struct ovsdb_symbol_table *symtab)
 {
+    enum ovsdb_atomic_type type = base->type;
+
     switch (type) {
     case OVSDB_TYPE_VOID:
         NOT_REACHED();
@@ -503,7 +533,9 @@ ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
 
     case OVSDB_TYPE_UUID:
         if (*s == '@') {
-            atom->uuid = ovsdb_symbol_table_insert(symtab, s)->uuid;
+            struct ovsdb_symbol *symbol = ovsdb_symbol_table_insert(symtab, s);
+            atom->uuid = symbol->uuid;
+            ovsdb_symbol_referenced(symbol, base);
         } else if (!uuid_from_string(&atom->uuid, s)) {
             return xasprintf("\"%s\" is not a valid UUID", s);
         }
@@ -535,7 +567,9 @@ ovsdb_atom_from_string__(union ovsdb_atom *atom, enum ovsdb_atomic_type type,
  *        then an identifier beginning with '@' is also acceptable.  If the
  *        named identifier is already in 'symtab', then the associated UUID is
  *        used; otherwise, a new, random UUID is used and added to the symbol
- *        table.
+ *        table.  If 'base' is a reference and a symbol is parsed, then the
+ *        symbol's 'strong_ref' or 'weak_ref' member is set to true, as
+ *        appropriate.
  *
  * Returns a null pointer if successful, otherwise an error message describing
  * the problem.  On failure, the contents of 'atom' are indeterminate.  The
@@ -549,7 +583,7 @@ ovsdb_atom_from_string(union ovsdb_atom *atom,
     struct ovsdb_error *error;
     char *msg;
 
-    msg = ovsdb_atom_from_string__(atom, base->type, s, symtab);
+    msg = ovsdb_atom_from_string__(atom, base, s, symtab);
     if (msg) {
         return msg;
     }
@@ -1829,6 +1863,8 @@ ovsdb_symbol_table_put(struct ovsdb_symbol_table *symtab, const char *name,
     symbol = xmalloc(sizeof *symbol);
     symbol->uuid = *uuid;
     symbol->created = created;
+    symbol->strong_ref = false;
+    symbol->weak_ref = false;
     shash_add(&symtab->sh, name, symbol);
     return symbol;
 }
index fe71f90..181df3b 100644 (file)
@@ -236,6 +236,8 @@ struct ovsdb_symbol_table {
 struct ovsdb_symbol {
     struct uuid uuid;           /* The UUID that the symbol represents. */
     bool created;               /* Already used to create row? */
+    bool strong_ref;            /* Parsed a strong reference to this row? */
+    bool weak_ref;              /* Parsed a weak reference to this row? */
 };
 
 struct ovsdb_symbol_table *ovsdb_symbol_table_create(void);
index 87f62d6..ef37d92 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010 Nicira Networks.
+/* Copyright (c) 2009, 2010, 2011 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -47,6 +47,7 @@ struct ovsdb_idl_column {
 
 struct ovsdb_idl_table_class {
     char *name;
+    bool is_root;
     const struct ovsdb_idl_column *columns;
     size_t n_columns;
     size_t allocation_size;
index d64812a..3a9af9f 100644 (file)
@@ -131,6 +131,7 @@ is represented by <database-schema>, as described below.
 
         "columns": {<id>: <column-schema>, ...}   required
         "maxRows": <integer>                      optional
+        "isRoot": <boolean>                       optional
 
     The value of "columns" is a JSON object whose names are column
     names and whose values are <column-schema>s.
@@ -152,12 +153,28 @@ is represented by <database-schema>, as described below.
         the database process is stopped and then started again, each
         "_version" also changes to a new random value.
 
+    If "isRoot" is omitted or specified as false, then any given row
+    in the table may exist only when there is at least one reference
+    to it, with refType "strong", from a different row (in the same
+    table or a different table).  This is a "deferred" action:
+    unreferenced rows in the table are deleted just before transaction
+    commit.  If "isRoot" is specified as true, then rows in the table
+    exist independent of any references (they can be thought of as
+    part of the "root set" in a garbage collector).
+
+    For compatibility with schemas created before "isRoot" was
+    introduced, if "isRoot" is omitted or false in every
+    <table-schema> in a given <database-schema>, then every table is
+    part of the root set.
+
     If "maxRows" is specified, as a positive integer, it limits the
     maximum number of rows that may be present in the table.  This is
     a "deferred" constraint, enforced only at transaction commit time
     (see the "transact" request below).  If "maxRows" is not
     specified, the size of the table is limited only by the resources
-    available to the database server.
+    available to the database server.  "maxRows" constraints are
+    enforced after unreferenced rows are deleted from tables with a
+    false "isRoot".
 
 <column-schema>
 
index caca9f8..52de5e8 100755 (executable)
@@ -1,6 +1,6 @@
 #! /usr/bin/perl
 
-# Copyright (c) 2009, 2010 Nicira Networks
+# Copyright (c) 2009, 2010, 2011 Nicira Networks
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -29,7 +29,14 @@ while (<>) {
         $y *= $scale;
         $width *= $scale;
         $height *= $scale;
+        print "linethick = ", ($style eq 'bold' ? 0.5 : 1.0), ";\n";
         print "box at $x,$y wid $width height $height \"$name\"\n";
+        if ($style eq 'bold') {
+            my $inset = 2.0 / 72.0;
+            $width -= $inset * 2;
+            $height -= $inset * 2;
+            print "box at $x,$y wid $width height $height\n";
+        }
     } elsif (/edge/) {
         my (undef, $tail, $head, $n, $rest) = split(' ', $_, 5);
         my @xy;
@@ -51,6 +58,8 @@ while (<>) {
         }
         my ($style, $color) = split(' ', $rest);
 
+        print "linethick = ", ($style eq 'dotted' ? 0.5 : 1), ";\n";
+
         print "spline -> from $xy[0][0],$xy[0][1]";
         for (my ($i) = 0; $i <= $#xy; $i++) {
             print " to $xy[$i][0],$xy[$i][1]";
index 5f30334..5ba4e71 100755 (executable)
@@ -290,9 +290,11 @@ Table      Purpose
 .SH "TABLE RELATIONSHIPS"
 .PP
 The following diagram shows the relationship among tables in the
-database.  Each node represents a table.  Each edge leads from the
+database.  Each node represents a table.  Tables that are part of the
+``root set'' are shown with double borders.  Each edge leads from the
 table that contains it and points to the table that its value
-represents.  Edges are labeled with their column names.
+represents.  Edges are labeled with their column names.  Thick lines
+represent strong references; thin lines represent weak references.
 .RS -1in
 """
         erStream = open(erFile, "r")
index 571ac8f..d417286 100755 (executable)
@@ -16,6 +16,7 @@ def printEdge(tableName, baseType, label):
         options['label'] = '"%s"' % label
         if baseType.ref_type == 'weak':
             options['constraint'] = 'false'
+            options['style'] = 'dotted'
         print "\t%s -> %s [%s];" % (
             tableName,
             baseType.ref_table,
@@ -25,12 +26,17 @@ def schemaToDot(schemaFile):
     schema = ovs.db.schema.DbSchema.from_json(ovs.json.from_file(schemaFile))
 
     print "digraph %s {" % schema.name
+    print '\tsize="6.5,4";'
+    print '\tmargin="0";'
+    print "\tnode [shape=box];"
+    print "\tedge [dir=none, arrowhead=none, arrowtail=none];"
     for tableName, table in schema.tables.iteritems():
-        print '\tsize="6.5,4";'
-        print '\tmargin="0";'
-        print "\tnode [shape=box];"
-        print "\tedge [dir=none, arrowhead=none, arrowtail=none];"
-        print "\t%s;" % tableName
+        options = {}
+        if table.is_root:
+            options['style'] = 'bold'
+        print "\t%s [%s];" % (
+            tableName,
+            ', '.join(['%s=%s' % (k,v) for k,v in options.items()]))
         for columnName, column in table.columns.iteritems():
             if column.type.value:
                 printEdge(tableName, column.type.key, "%s key" % columnName)
index 4183089..2a4c67c 100755 (executable)
@@ -487,7 +487,11 @@ static void\n%s_columns_init(void)
     print "struct ovsdb_idl_table_class %stable_classes[%sN_TABLES] = {" % (prefix, prefix.upper())
     for tableName, table in sorted(schema.tables.iteritems()):
         structName = "%s%s" % (prefix, tableName.lower())
-        print "    {\"%s\"," % tableName
+        if table.is_root:
+            is_root = "true"
+        else:
+            is_root = "false"
+        print "    {\"%s\", %s," % (tableName, is_root)
         print "     %s_columns, ARRAY_SIZE(%s_columns)," % (
             structName, structName)
         print "     sizeof(struct %s)}," % structName
index e76544e..aad8415 100644 (file)
@@ -127,6 +127,21 @@ is_valid_version(const char *s)
     return n != -1 && s[n] == '\0';
 }
 
+/* Returns the number of tables in 'schema''s root set. */
+static size_t
+root_set_size(const struct ovsdb_schema *schema)
+{
+    struct shash_node *node;
+    size_t n_root;
+
+    SHASH_FOR_EACH (node, &schema->tables) {
+        struct ovsdb_table_schema *table = node->data;
+
+        n_root += table->is_root;
+    }
+    return n_root;
+}
+
 struct ovsdb_error *
 ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap)
 {
@@ -205,6 +220,18 @@ ovsdb_schema_from_json(struct json *json, struct ovsdb_schema **schemap)
         }
     }
 
+    /* "isRoot" was not part of the original schema definition.  Before it was
+     * added, there was no support for garbage collection.  So, for backward
+     * compatibility, if the root set is empty then assume that every table is
+     * in the root set. */
+    if (root_set_size(schema) == 0) {
+        SHASH_FOR_EACH (node, &schema->tables) {
+            struct ovsdb_table_schema *table = node->data;
+
+            table->is_root = true;
+        }
+    }
+
     *schemap = schema;
     return 0;
 }
@@ -214,6 +241,7 @@ ovsdb_schema_to_json(const struct ovsdb_schema *schema)
 {
     struct json *json, *tables;
     struct shash_node *node;
+    bool default_is_root;
 
     json = json_object_create();
     json_object_put_string(json, "name", schema->name);
@@ -224,12 +252,18 @@ ovsdb_schema_to_json(const struct ovsdb_schema *schema)
         json_object_put_string(json, "cksum", schema->cksum);
     }
 
+    /* "isRoot" was not part of the original schema definition.  Before it was
+     * added, there was no support for garbage collection.  So, for backward
+     * compatibility, if every table is in the root set then do not output
+     * "isRoot" in table schemas. */
+    default_is_root = root_set_size(schema) == shash_count(&schema->tables);
+
     tables = json_object_create();
 
     SHASH_FOR_EACH (node, &schema->tables) {
         struct ovsdb_table_schema *table = node->data;
         json_object_put(tables, table->name,
-                        ovsdb_table_schema_to_json(table));
+                        ovsdb_table_schema_to_json(table, default_is_root));
     }
     json_object_put(json, "tables", tables);
 
index 5e83683..2f69350 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira Networks
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@ add_column(struct ovsdb_table_schema *ts, struct ovsdb_column *column)
 
 struct ovsdb_table_schema *
 ovsdb_table_schema_create(const char *name, bool mutable,
-                          unsigned int max_rows)
+                          unsigned int max_rows, bool is_root)
 {
     struct ovsdb_column *uuid, *version;
     struct ovsdb_table_schema *ts;
@@ -47,6 +47,7 @@ ovsdb_table_schema_create(const char *name, bool mutable,
     ts->mutable = mutable;
     shash_init(&ts->columns);
     ts->max_rows = max_rows;
+    ts->is_root = is_root;
 
     uuid = ovsdb_column_create("_uuid", false, true, &ovsdb_type_uuid);
     add_column(ts, uuid);
@@ -65,7 +66,8 @@ ovsdb_table_schema_clone(const struct ovsdb_table_schema *old)
     struct ovsdb_table_schema *new;
     struct shash_node *node;
 
-    new = ovsdb_table_schema_create(old->name, old->mutable, old->max_rows);
+    new = ovsdb_table_schema_create(old->name, old->mutable,
+                                    old->max_rows, old->is_root);
     SHASH_FOR_EACH (node, &old->columns) {
         const struct ovsdb_column *column = node->data;
 
@@ -97,7 +99,7 @@ ovsdb_table_schema_from_json(const struct json *json, const char *name,
                              struct ovsdb_table_schema **tsp)
 {
     struct ovsdb_table_schema *ts;
-    const struct json *columns, *mutable, *max_rows;
+    const struct json *columns, *mutable, *max_rows, *is_root;
     struct shash_node *node;
     struct ovsdb_parser parser;
     struct ovsdb_error *error;
@@ -111,6 +113,7 @@ ovsdb_table_schema_from_json(const struct json *json, const char *name,
                                   OP_TRUE | OP_FALSE | OP_OPTIONAL);
     max_rows = ovsdb_parser_member(&parser, "maxRows",
                                    OP_INTEGER | OP_OPTIONAL);
+    is_root = ovsdb_parser_member(&parser, "isRoot", OP_BOOLEAN | OP_OPTIONAL);
     error = ovsdb_parser_finish(&parser);
     if (error) {
         return error;
@@ -133,7 +136,8 @@ ovsdb_table_schema_from_json(const struct json *json, const char *name,
 
     ts = ovsdb_table_schema_create(name,
                                    mutable ? json_boolean(mutable) : true,
-                                   MIN(n_max_rows, UINT_MAX));
+                                   MIN(n_max_rows, UINT_MAX),
+                                   is_root ? json_boolean(is_root) : false);
     SHASH_FOR_EACH (node, json_object(columns)) {
         struct ovsdb_column *column;
 
@@ -156,8 +160,19 @@ ovsdb_table_schema_from_json(const struct json *json, const char *name,
     return 0;
 }
 
+/* Returns table schema 'ts' serialized into JSON.
+ *
+ * The "isRoot" member is included in the JSON only if its value would differ
+ * from 'default_is_root'.  Ordinarily 'default_is_root' should be false,
+ * because ordinarily a table would be not be part of the root set if its
+ * "isRoot" member is omitted.  However, garbage collection was not orginally
+ * included in OVSDB, so in older schemas that do not include any "isRoot"
+ * members, every table is implicitly part of the root set.  To serialize such
+ * a schema in a way that can be read by older OVSDB tools, specify
+ * 'default_is_root' as true. */
 struct json *
-ovsdb_table_schema_to_json(const struct ovsdb_table_schema *ts)
+ovsdb_table_schema_to_json(const struct ovsdb_table_schema *ts,
+                           bool default_is_root)
 {
     struct json *json, *columns;
     struct shash_node *node;
@@ -166,6 +181,9 @@ ovsdb_table_schema_to_json(const struct ovsdb_table_schema *ts)
     if (!ts->mutable) {
         json_object_put(json, "mutable", json_boolean_create(false));
     }
+    if (default_is_root != ts->is_root) {
+        json_object_put(json, "isRoot", json_boolean_create(ts->is_root));
+    }
 
     columns = json_object_create();
 
index 4d3b9ee..95da740 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010 Nicira Networks
+/* Copyright (c) 2009, 2010, 2011 Nicira Networks
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,11 +30,11 @@ struct ovsdb_table_schema {
     bool mutable;
     struct shash columns;       /* Contains "struct ovsdb_column *"s. */
     unsigned int max_rows;      /* Maximum number of rows. */
+    bool is_root;               /* Part of garbage collection root set? */
 };
 
-struct ovsdb_table_schema *ovsdb_table_schema_create(const char *name,
-                                                     bool mutable,
-                                                     unsigned int max_rows);
+struct ovsdb_table_schema *ovsdb_table_schema_create(
+    const char *name, bool mutable, unsigned int max_rows, bool is_root);
 struct ovsdb_table_schema *ovsdb_table_schema_clone(
     const struct ovsdb_table_schema *);
 void ovsdb_table_schema_destroy(struct ovsdb_table_schema *);
@@ -43,7 +43,8 @@ struct ovsdb_error *ovsdb_table_schema_from_json(const struct json *,
                                                  const char *name,
                                                  struct ovsdb_table_schema **)
     WARN_UNUSED_RESULT;
-struct json *ovsdb_table_schema_to_json(const struct ovsdb_table_schema *);
+struct json *ovsdb_table_schema_to_json(const struct ovsdb_table_schema *,
+                                        bool default_is_root);
 
 const struct ovsdb_column *ovsdb_table_schema_get_column(
     const struct ovsdb_table_schema *, const char *name);
index 615c164..c07541e 100644 (file)
@@ -81,6 +81,8 @@ struct ovsdb_txn_row {
     unsigned long changed[];    /* Bits set to 1 for columns that changed. */
 };
 
+static struct ovsdb_error * WARN_UNUSED_RESULT
+delete_garbage_row(struct ovsdb_txn *txn, struct ovsdb_txn_row *r);
 static void ovsdb_txn_row_prefree(struct ovsdb_txn_row *);
 static struct ovsdb_error * WARN_UNUSED_RESULT
 for_each_txn_row(struct ovsdb_txn *txn,
@@ -158,6 +160,20 @@ find_txn_row(const struct ovsdb_table *table, const struct uuid *uuid)
     return NULL;
 }
 
+static struct ovsdb_txn_row *
+find_or_make_txn_row(struct ovsdb_txn *txn, const struct ovsdb_table *table,
+                     const struct uuid *uuid)
+{
+    struct ovsdb_txn_row *txn_row = find_txn_row(table, uuid);
+    if (!txn_row) {
+        const struct ovsdb_row *row = ovsdb_table_get_row(table, uuid);
+        if (row) {
+            txn_row = ovsdb_txn_row_modify(txn, row)->txn_row;
+        }
+    }
+    return txn_row;
+}
+
 static struct ovsdb_error * WARN_UNUSED_RESULT
 ovsdb_txn_adjust_atom_refs(struct ovsdb_txn *txn, const struct ovsdb_row *r,
                            const struct ovsdb_column *c,
@@ -175,24 +191,22 @@ ovsdb_txn_adjust_atom_refs(struct ovsdb_txn *txn, const struct ovsdb_row *r,
     table = base->u.uuid.refTable;
     for (i = 0; i < n; i++) {
         const struct uuid *uuid = &atoms[i].uuid;
-        struct ovsdb_txn_row *txn_row = find_txn_row(table, uuid);
+        struct ovsdb_txn_row *txn_row;
+
         if (uuid_equals(uuid, ovsdb_row_get_uuid(r))) {
             /* Self-references don't count. */
             continue;
         }
+
+        txn_row = find_or_make_txn_row(txn, table, uuid);
         if (!txn_row) {
-            const struct ovsdb_row *row = ovsdb_table_get_row(table, uuid);
-            if (row) {
-                txn_row = ovsdb_txn_row_modify(txn, row)->txn_row;
-            } else {
-                return ovsdb_error("referential integrity violation",
-                                   "Table %s column %s row "UUID_FMT" "
-                                   "references nonexistent row "UUID_FMT" in "
-                                   "table %s.",
-                                   r->table->schema->name, c->name,
-                                   UUID_ARGS(ovsdb_row_get_uuid(r)),
-                                   UUID_ARGS(uuid), table->schema->name);
-            }
+            return ovsdb_error("referential integrity violation",
+                               "Table %s column %s row "UUID_FMT" "
+                               "references nonexistent row "UUID_FMT" in "
+                               "table %s.",
+                               r->table->schema->name, c->name,
+                               UUID_ARGS(ovsdb_row_get_uuid(r)),
+                               UUID_ARGS(uuid), table->schema->name);
         }
         txn_row->n_refs += delta;
     }
@@ -257,6 +271,92 @@ check_ref_count(struct ovsdb_txn *txn OVS_UNUSED, struct ovsdb_txn_row *r)
     }
 }
 
+static struct ovsdb_error * WARN_UNUSED_RESULT
+delete_row_refs(struct ovsdb_txn *txn, const struct ovsdb_row *row,
+                const struct ovsdb_base_type *base,
+                const union ovsdb_atom *atoms, unsigned int n)
+{
+    const struct ovsdb_table *table;
+    unsigned int i;
+
+    if (!ovsdb_base_type_is_strong_ref(base)) {
+        return NULL;
+    }
+
+    table = base->u.uuid.refTable;
+    for (i = 0; i < n; i++) {
+        const struct uuid *uuid = &atoms[i].uuid;
+        struct ovsdb_txn_row *txn_row;
+
+        if (uuid_equals(uuid, ovsdb_row_get_uuid(row))) {
+            /* Self-references don't count. */
+            continue;
+        }
+
+        txn_row = find_or_make_txn_row(txn, table, uuid);
+        if (!txn_row) {
+            return OVSDB_BUG("strong ref target missing");
+        } else if (!txn_row->n_refs) {
+            return OVSDB_BUG("strong ref target has zero n_refs");
+        } else if (!txn_row->new) {
+            return OVSDB_BUG("deleted strong ref target");
+        }
+
+        if (--txn_row->n_refs == 0) {
+            struct ovsdb_error *error = delete_garbage_row(txn, txn_row);
+            if (error) {
+                return error;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+static struct ovsdb_error * WARN_UNUSED_RESULT
+delete_garbage_row(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
+{
+    struct shash_node *node;
+    struct ovsdb_row *row;
+
+    if (txn_row->table->schema->is_root) {
+        return NULL;
+    }
+
+    row = txn_row->new;
+    txn_row->new = NULL;
+    hmap_remove(&txn_row->table->rows, &row->hmap_node);
+    SHASH_FOR_EACH (node, &txn_row->table->schema->columns) {
+        const struct ovsdb_column *column = node->data;
+        const struct ovsdb_datum *field = &row->fields[column->index];
+        struct ovsdb_error *error;
+
+        error = delete_row_refs(txn, row,
+                                &column->type.key, field->keys, field->n);
+        if (error) {
+            return error;
+        }
+
+        error = delete_row_refs(txn, row,
+                                &column->type.value, field->values, field->n);
+        if (error) {
+            return error;
+        }
+    }
+    ovsdb_row_destroy(row);
+
+    return NULL;
+}
+
+static struct ovsdb_error * WARN_UNUSED_RESULT
+collect_garbage(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
+{
+    if (txn_row->new && !txn_row->n_refs) {
+        return delete_garbage_row(txn, txn_row);
+    }
+    return NULL;
+}
+
 static struct ovsdb_error * WARN_UNUSED_RESULT
 update_ref_counts(struct ovsdb_txn *txn)
 {
@@ -491,15 +591,22 @@ ovsdb_txn_commit(struct ovsdb_txn *txn, bool durable)
         return NULL;
     }
 
-    /* Check maximum rows table constraints. */
-    error = check_max_rows(txn);
+    /* Update reference counts and check referential integrity. */
+    error = update_ref_counts(txn);
     if (error) {
         ovsdb_txn_abort(txn);
         return error;
     }
 
-    /* Update reference counts and check referential integrity. */
-    error = update_ref_counts(txn);
+    /* Delete unreferenced, non-root rows. */
+    error = for_each_txn_row(txn, collect_garbage);
+    if (error) {
+        ovsdb_txn_abort(txn);
+        return OVSDB_WRAP_BUG("can't happen", error);
+    }
+
+    /* Check maximum rows table constraints. */
+    error = check_max_rows(txn);
     if (error) {
         ovsdb_txn_abort(txn);
         return error;
index c12eda2..e0e0daf 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010 Nicira Networks
+# Copyright (c) 2009, 2010, 2011 Nicira Networks
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -34,6 +34,22 @@ class DbSchema(object):
                 self.__check_ref_table(column, column.type.key, "key")
                 self.__check_ref_table(column, column.type.value, "value")
 
+        # "isRoot" was not part of the original schema definition.  Before it
+        # was added, there was no support for garbage collection.  So, for
+        # backward compatibility, if the root set is empty then assume that
+        # every table is in the root set.
+        if self.__root_set_size() == 0:
+            for table in self.tables.itervalues():
+                table.is_root = True
+
+    def __root_set_size(self):
+        """Returns the number of tables in the schema's root set."""
+        n_root = 0
+        for table in self.tables.itervalues():
+            if table.is_root:
+                n_root += 1
+        return n_root
+
     @staticmethod
     def from_json(json):
         parser = ovs.db.parser.Parser(json, "database schema")
@@ -60,9 +76,15 @@ class DbSchema(object):
         return DbSchema(name, version, tables)
 
     def to_json(self):
+        # "isRoot" was not part of the original schema definition.  Before it
+        # was added, there was no support for garbage collection.  So, for
+        # backward compatibility, if every table is in the root set then do not
+        # output "isRoot" in table schemas.
+        default_is_root = self.__root_set_size() == len(self.tables)
+
         tables = {}
         for table in self.tables.itervalues():
-            tables[table.name] = table.to_json()
+            tables[table.name] = table.to_json(default_is_root)
         json = {"name": self.name, "tables": tables}
         if self.version:
             json["version"] = self.version
@@ -96,11 +118,13 @@ class IdlSchema(DbSchema):
                          idlPrefix, idlHeader)
 
 class TableSchema(object):
-    def __init__(self, name, columns, mutable=True, max_rows=sys.maxint):
+    def __init__(self, name, columns, mutable=True, max_rows=sys.maxint,
+                 is_root=True):
         self.name = name
         self.columns = columns
         self.mutable = mutable
-        self.max_rows = max_rows        
+        self.max_rows = max_rows
+        self.is_root = is_root
 
     @staticmethod
     def from_json(json, name):
@@ -108,6 +132,7 @@ class TableSchema(object):
         columnsJson = parser.get("columns", [dict])
         mutable = parser.get_optional("mutable", [bool], True)
         max_rows = parser.get_optional("maxRows", [int])
+        is_root = parser.get_optional("isRoot", [bool], False)
         parser.finish()
 
         if max_rows == None:
@@ -128,12 +153,25 @@ class TableSchema(object):
             columns[columnName] = ColumnSchema.from_json(columnJson,
                                                          columnName)
 
-        return TableSchema(name, columns, mutable, max_rows)
+        return TableSchema(name, columns, mutable, max_rows, is_root)
 
-    def to_json(self):
+    def to_json(self, default_is_root=False):
+        """Returns this table schema serialized into JSON.
+
+        The "isRoot" member is included in the JSON only if its value would
+        differ from 'default_is_root'.  Ordinarily 'default_is_root' should be
+        false, because ordinarily a table would be not be part of the root set
+        if its "isRoot" member is omitted.  However, garbage collection was not
+        orginally included in OVSDB, so in older schemas that do not include
+        any "isRoot" members, every table is implicitly part of the root set.
+        To serialize such a schema in a way that can be read by older OVSDB
+        tools, specify 'default_is_root' as True.
+        """
         json = {}
         if not self.mutable:
             json["mutable"] = False
+        if default_is_root != self.is_root:
+            json["isRoot"] = self.is_root
 
         json["columns"] = columns = {}
         for column in self.columns.itervalues():
index b0c1026..2e8af85 100644 (file)
@@ -535,7 +535,9 @@ AT_BANNER([ovs-vsctl unit tests -- database commands])
 AT_SETUP([database commands -- positive checks])
 AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
-AT_CHECK([RUN_OVS_VSCTL([create b name=br0])], 
+AT_CHECK(
+  [RUN_OVS_VSCTL_TOGETHER([--id=@br0 create b name=br0],
+                          [set o . bridges=@br0])],
   [0], [stdout], [], [OVS_VSCTL_CLEANUP])
 cp stdout out1
 AT_CHECK([RUN_OVS_VSCTL([list b], [get b br0 _uuid])], 
@@ -543,6 +545,7 @@ AT_CHECK([RUN_OVS_VSCTL([list b], [get b br0 _uuid])],
 cp stdout out2
 AT_CHECK([perl $srcdir/uuidfilt.pl out1 out2], [0], 
   [[<0>
+
 _uuid               : <0>
 controller          : []
 datapath_id         : []
@@ -572,8 +575,10 @@ AT_CHECK(
 name                : "br0"
 datapath_type       : ""
 ]], [ignore], [test ! -e pid || kill `cat pid`])
-AT_CHECK([RUN_OVS_VSCTL([create b name=br1 datapath_type="foo"],
-                        [create b name=br2 external-ids:bar=quux])],
+AT_CHECK([
+  RUN_OVS_VSCTL_TOGETHER([--id=@br1 create b name=br1 datapath_type="foo"],
+                         [--id=@br2 create b name=br2 external-ids:bar=quux],
+                         [add o . bridges @br1 @br2])],
   [0], [stdout], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK(
   [RUN_OVS_VSCTL([--columns=name find b datapath_type!=foo])], [0], [stdout],
@@ -608,7 +613,8 @@ AT_CHECK([RUN_OVS_VSCTL([clear br br0 external-ids -- get br br0 external_ids])]
 ], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL_TOGETHER([destroy b br0],
                                  [destroy b br1],
-                                 [destroy b br2])],
+                                 [destroy b br2],
+                                 [clear o . bridges])],
   [0], [stdout], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([list b])], 
   [0], [], [], [OVS_VSCTL_CLEANUP])
@@ -618,19 +624,22 @@ AT_CLEANUP
 AT_SETUP([database commands -- negative checks])
 AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
-AT_CHECK([RUN_OVS_VSCTL([create b name=br0])], 
+AT_CHECK([RUN_OVS_VSCTL([add-br br0])],
   [0], [ignore], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([add-br br1])], 
   [0], [ignore], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([RUN_OVS_VSCTL([set-controller br1 tcp:127.0.0.1])], 
   [0], [ignore], [], [OVS_VSCTL_CLEANUP])
-AT_CHECK([RUN_OVS_VSCTL([create n targets='"1.2.3.4:567"'])], 
+AT_CHECK([
+    RUN_OVS_VSCTL_TOGETHER([--id=@n create n targets='"1.2.3.4:567"'],
+                           [set bridge br0 netflow=@n])],
   [0], [stdout], [], [OVS_VSCTL_CLEANUP])
 cp stdout netflow-uuid
 AT_CHECK([RUN_OVS_VSCTL([list n `cat netflow-uuid`])],
   [0], [stdout], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([perl $srcdir/uuidfilt.pl netflow-uuid stdout], [0], 
   [[<0>
+
 _uuid               : <0>
 active_timeout      : 0
 add_id_to_interface : false
@@ -806,13 +815,44 @@ name                : "br0"
 OVS_VSCTL_CLEANUP
 AT_CLEANUP
 
+AT_SETUP([unreferenced record warnings])
+AT_KEYWORDS([ovs-vsctl])
+OVS_VSCTL_SETUP
+AT_CHECK(
+  [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket \
+     -- create Bridge name=br0 | $srcdir/uuidfilt.pl],
+  [0], [<0>
+], [vsctl|WARN|applying "create" command to table Bridge without --id option will have no effect
+], [OVS_VSCTL_CLEANUP])
+AT_CHECK(
+  [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket \
+     -- --id=@br0 create Bridge name=br0 | $srcdir/uuidfilt.pl],
+  [0], [<0>
+], [vsctl|WARN|row id "@br0" was created but no reference to it was inserted, so it will not actually appear in the database
+], [OVS_VSCTL_CLEANUP])
+AT_CHECK(
+  [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket \
+     -- --id=@eth0_iface create Interface name=eth0 \
+     -- --id=@eth0 create Port name=eth0 interfaces=@eth0_iface \
+     -- --id=@m0 create Mirror name=m0 output_port=@eth0 \
+     -- --id=@br0 create Bridge name=br0 mirrors=@m0 \
+     -- set Open_vSwitch . bridges=@br0 | $srcdir/uuidfilt.pl],
+  [0], [<0>
+<1>
+<2>
+<3>
+], [vsctl|WARN|row id "@eth0" was created but only a weak reference to it was inserted, so it will not actually appear in the database
+], [OVS_VSCTL_CLEANUP])
+OVS_VSCTL_CLEANUP
+AT_CLEANUP
+
 dnl This test really shows a bug -- "create" followed by "list" in
 dnl the same execution shows the wrong UUID on the "list" command.
 dnl The bug is documented in ovs-vsctl.8.
 AT_SETUP([created row UUID is wrong in same execution])
 AT_KEYWORDS([ovs-vsctl])
 OVS_VSCTL_SETUP
-AT_CHECK([RUN_OVS_VSCTL([create Bridge name=br0 -- list b])], 
+AT_CHECK([RUN_OVS_VSCTL([--id=@br0 create Bridge name=br0 -- add Open_vSwitch . bridges @br0 -- list b])],
   [0], [stdout], [], [OVS_VSCTL_CLEANUP])
 AT_CHECK([perl $srcdir/uuidfilt.pl stdout], [0], 
   [[<0>
index f98519f..ebf1186 100644 (file)
@@ -57,6 +57,44 @@ m4_define([WEAK_SCHEMA],
                                     "refType": "weak"},
                             "min": 0, "max": "unlimited"}}}}}}]])
 
+m4_define([GC_SCHEMA],
+  [[{"name": "gc",
+     "tables": {
+       "root": {
+         "columns": {
+           "a": {"type": {"key": {"type": "uuid",
+                                  "refTable": "a"},
+                            "min": 0, "max": "unlimited"}}},
+         "isRoot": true},
+       "a": {
+         "columns": {
+           "a": {"type": "integer"},
+           "a2a": {"type": {"key": {"type": "uuid",
+                                    "refTable": "a"},
+                            "min": 0, "max": "unlimited"}},
+           "a2b": {"type": {"key": {"type": "uuid",
+                                    "refTable": "b"},
+                            "min": 0, "max": "unlimited"}},
+           "wa2a": {"type": {"key": {"type": "uuid",
+                                     "refTable": "a",
+                                     "refType": "weak"},
+                             "min": 0, "max": "unlimited"}},
+           "wa2b": {"type": {"key": {"type": "uuid",
+                                    "refTable": "b",
+                                    "refType": "weak"},
+                             "min": 0, "max": "unlimited"}}}},
+       "b": {
+         "columns": {
+           "b": {"type": "integer"},
+           "b2a": {"type": {"key": {"type": "uuid",
+                                    "refTable": "a"},
+                            "min": 0, "max": "unlimited"}},
+           "wb2a": {"type": {"key": {"type": "uuid",
+                                     "refTable": "a",
+                                     "refType": "weak"},
+                             "min": 0, "max": "unlimited"}}},
+         "isRoot": false}}}]])
+
 # OVSDB_CHECK_EXECUTION(TITLE, SCHEMA, TRANSACTIONS, OUTPUT, [KEYWORDS])
 #
 # Runs "test-ovsdb execute" with the given SCHEMA and each of the
@@ -760,6 +798,150 @@ OVSDB_CHECK_EXECUTION([weak references],
 [{"count":1}]
 [{"rows":[]}]
 [{"rows":[{"_uuid":["uuid","<3>"],"b":2,"b2a":["set",[]]},{"_uuid":["uuid","<4>"],"b":3,"b2a":["set",[]]}]}]
+]])
+
+OVSDB_CHECK_EXECUTION([garbage collection],
+  [GC_SCHEMA],
+  [dnl Check that inserting a row without any references is a no-op.
+   [[["gc",
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 0}}]]],
+   [[["gc",
+      {"op": "select",
+       "table": "a",
+       "where": [],
+       "columns": ["a"]}]]],
+   dnl Check that inserting a chain of rows that reference each other
+   dnl in turn is also a no-op.
+   [[["gc",
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 0, "a2a": ["named-uuid", "row1"]},
+       "uuid-name": "row0"},
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 1, "a2a": ["named-uuid", "row2"]},
+       "uuid-name": "row1"},
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 2, "a2a": ["named-uuid", "row3"]},
+       "uuid-name": "row2"},
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 3},
+       "uuid-name": "row3"}]]],
+   [[["gc",
+      {"op": "select",
+       "table": "a",
+       "where": [],
+       "columns": ["a"]}]]],
+   dnl Check that inserting a pair of rows that mutually reference each
+   dnl other causes the rows to be retained.
+   [[["gc",
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 4, "a2a": ["named-uuid", "row5"]},
+       "uuid-name": "row4"},
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 5, "a2a": ["named-uuid", "row4"]},
+       "uuid-name": "row5"}]]],
+   [[["gc",
+      {"op": "select",
+       "table": "a",
+       "where": [],
+       "columns": ["a"],
+       "sort": ["a"]}]]],
+   dnl Check that unreferencing one of the rows causes the other to be deleted.
+   [[["gc",
+      {"op": "update",
+       "table": "a",
+       "where": [["a", "==", 4]],
+       "row": {"a2a": ["set", []]}}]]],
+   [[["gc",
+      {"op": "select",
+       "table": "a",
+       "where": [],
+       "columns": ["a"]}]]],
+   dnl Check that inserting a pair of rows that mutually weak reference each
+   dnl other is a no-op.
+   [[["gc",
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 6, "wa2a": ["named-uuid", "row7"]},
+       "uuid-name": "row6"},
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 7, "wa2a": ["named-uuid", "row6"]},
+       "uuid-name": "row7"}]]],
+   [[["gc",
+      {"op": "select",
+       "table": "a",
+       "where": [],
+       "columns": ["a"]}]]],
+   dnl Check that a circular chain of rows is retained.
+   [[["gc",
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 8, "a2a": ["named-uuid", "row9"]},
+       "uuid-name": "row8"},
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 9, "a2a": ["named-uuid", "row10"]},
+       "uuid-name": "row9"},
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 10, "a2a": ["named-uuid", "row11"]},
+       "uuid-name": "row10"},
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 11, "a2a": ["named-uuid", "row8"]},
+       "uuid-name": "row11"}]]],
+   [[["gc",
+      {"op": "select",
+       "table": "a",
+       "where": [],
+       "columns": ["a"],
+       "sort": ["a"]}]]],
+   dnl Check that breaking the chain causes all of the rows to be deleted.
+   [[["gc",
+      {"op": "update",
+       "table": "a",
+       "where": [["a", "==", 9]],
+       "row": {"a2a": ["set", []]}}]]],
+   [[["gc",
+      {"op": "select",
+       "table": "a",
+       "where": [],
+       "columns": ["a"]}]]],
+   dnl Check that inserting a row only referenced by itself is a no-op.
+   [[["gc",
+      {"op": "insert",
+       "table": "a",
+       "row": {"a": 12, "a2a": ["named-uuid", "self"]},
+       "uuid-name": "self"}]]],
+   [[["gc",
+      {"op": "select",
+       "table": "a",
+       "where": [],
+       "columns": ["a"]}]]]],
+  [[[{"uuid":["uuid","<0>"]}]
+[{"rows":[]}]
+[{"uuid":["uuid","<1>"]},{"uuid":["uuid","<2>"]},{"uuid":["uuid","<3>"]},{"uuid":["uuid","<4>"]}]
+[{"rows":[]}]
+[{"uuid":["uuid","<5>"]},{"uuid":["uuid","<6>"]}]
+[{"rows":[{"a":4},{"a":5}]}]
+[{"count":1}]
+[{"rows":[]}]
+[{"uuid":["uuid","<7>"]},{"uuid":["uuid","<8>"]}]
+[{"rows":[]}]
+[{"uuid":["uuid","<9>"]},{"uuid":["uuid","<10>"]},{"uuid":["uuid","<11>"]},{"uuid":["uuid","<12>"]}]
+[{"rows":[{"a":8},{"a":9},{"a":10},{"a":11}]}]
+[{"count":1}]
+[{"rows":[]}]
+[{"uuid":["uuid","<13>"]}]
+[{"rows":[]}]
 ]])])
 
 EXECUTION_EXAMPLES
index 70f8ac2..cf206c4 100644 (file)
@@ -1,6 +1,6 @@
 AT_BANNER([OVSDB -- tables])
 
-OVSDB_CHECK_POSITIVE_CPY([table with one column],
+OVSDB_CHECK_POSITIVE_CPY([non-root table with one column],
   [[parse-table mytable '{"columns": {"name": {"type": "string"}}}']],
   [[{"columns":{"name":{"type":"string"}}}]])
 
@@ -10,6 +10,22 @@ OVSDB_CHECK_POSITIVE_CPY([immutable table with one column],
       "mutable": false}']],
   [[{"columns":{"name":{"type":"string"}},"mutable":false}]])
 
+OVSDB_CHECK_POSITIVE_CPY([root table with one column],
+  [[parse-table mytable \
+    '{"columns": {"name": {"type": "string"}},
+      "isRoot": true}']],
+  [[{"columns":{"name":{"type":"string"}},"isRoot":true}]])
+
+OVSDB_CHECK_POSITIVE_CPY([non-root table with default_is_root=true],
+  [[parse-table mytable '{"columns": {"name": {"type": "string"}}}' true]],
+  [[{"columns":{"name":{"type":"string"}},"isRoot":false}]])
+
+OVSDB_CHECK_POSITIVE_CPY([root table with default_is_root=true],
+  [[parse-table mytable \
+    '{"columns": {"name": {"type": "string"}},
+      "isRoot": true}' true]],
+  [[{"columns":{"name":{"type":"string"}}}]])
+
 OVSDB_CHECK_POSITIVE_CPY([table with maxRows of 2],
   [[parse-table mytable '{"columns": {"name": {"type": "string"}}, 
                           "maxRows": 2}']],
index c424abe..990bf6d 100644 (file)
@@ -141,7 +141,7 @@ usage(void)
            "    parse string DATUMs as data of given TYPE, and re-serialize\n"
            "  parse-column NAME OBJECT\n"
            "    parse column NAME with info OBJECT, and re-serialize\n"
-           "  parse-table NAME OBJECT\n"
+           "  parse-table NAME OBJECT [DEFAULT-IS-ROOT]\n"
            "    parse table NAME with info OBJECT\n"
            "  parse-row TABLE ROW..., and re-serialize\n"
            "    parse each ROW of defined TABLE\n"
@@ -608,12 +608,15 @@ static void
 do_parse_table(int argc OVS_UNUSED, char *argv[])
 {
     struct ovsdb_table_schema *ts;
+    bool default_is_root;
     struct json *json;
 
+    default_is_root = argc > 3 && !strcmp(argv[3], "true");
+
     json = parse_json(argv[2]);
     check_ovsdb_error(ovsdb_table_schema_from_json(json, argv[1], &ts));
     json_destroy(json);
-    print_and_free_json(ovsdb_table_schema_to_json(ts));
+    print_and_free_json(ovsdb_table_schema_to_json(ts, default_is_root));
     ovsdb_table_schema_destroy(ts);
 }
 
@@ -1932,7 +1935,7 @@ static struct command all_commands[] = {
     { "parse-data-strings", 2, INT_MAX, do_parse_data_strings },
     { "sort-atoms", 2, 2, do_sort_atoms },
     { "parse-column", 2, 2, do_parse_column },
-    { "parse-table", 2, 2, do_parse_table },
+    { "parse-table", 2, 3, do_parse_table },
     { "parse-rows", 2, INT_MAX, do_parse_rows },
     { "compare-rows", 2, INT_MAX, do_compare_rows },
     { "parse-conditions", 2, INT_MAX, do_parse_conditions },
index 863bcb8..2eafe99 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2009, 2010 Nicira Networks
+# Copyright (c) 2009, 2010, 2011 Nicira Networks
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -121,10 +121,11 @@ def do_parse_column(name, column_string):
     column = ovs.db.schema.ColumnSchema.from_json(column_json, name)
     print ovs.json.to_string(column.to_json(), sort_keys=True)
 
-def do_parse_table(name, table_string):
+def do_parse_table(name, table_string, default_is_root_string='false'):
+    default_is_root = default_is_root_string == 'true'
     table_json = unbox_json(ovs.json.from_string(table_string))
     table = ovs.db.schema.TableSchema.from_json(table_json, name)
-    print ovs.json.to_string(table.to_json(), sort_keys=True)
+    print ovs.json.to_string(table.to_json(default_is_root), sort_keys=True)
 
 def do_parse_rows(table_string, *rows):
     table_json = unbox_json(ovs.json.from_string(table_string))
@@ -272,7 +273,7 @@ parse-data TYPE DATUM...
   parse JSON DATUMs as data of given TYPE, and re-serialize
 parse-column NAME OBJECT
   parse column NAME with info OBJECT, and re-serialize
-parse-table NAME OBJECT
+parse-table NAME OBJECT [DEFAULT-IS-ROOT]
   parse table NAME with info OBJECT
 parse-schema JSON
   parse JSON as an OVSDB schema, and re-serialize
@@ -332,7 +333,7 @@ def main(argv):
                 "parse-data": (do_parse_data, (2,)),
                 "sort-atoms": (do_sort_atoms, 2),
                 "parse-column": (do_parse_column, 2),
-                "parse-table": (do_parse_table, 2),
+                "parse-table": (do_parse_table, (2, 3)),
                 "parse-schema": (do_parse_schema, 1),
                 "idl": (do_idl, (1,))}
 
index 3b35179..ee76b83 100644 (file)
@@ -478,9 +478,11 @@ A bridge port.  Records may be identified by port name.
 A network device attached to a port.  Records may be identified by
 name.
 .IP "\fBQoS\fR"
-Quality-of-service configuration for an \fBInterface\fR.
+Quality-of-service configuration for a \fBPort\fR.  Records may be
+identified by port name.
 .IP "\fBQueue\fR"
-Configuration for one queue within a \fBQoS\fR configuration.
+Configuration for one queue within a \fBQoS\fR configuration.  Records
+may only be identified by UUID.
 .IP "\fBMonitor\fR"
 Connectivity Monitoring attached to an \fBInterface\fR.  Records may be
 identified by \fBInterface\fR name.
@@ -637,10 +639,30 @@ If \fB@\fIname\fR is specified, then the UUID for the new row may be
 referred to by that name elsewhere in the same \fBovs\-vsctl\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 \fBovs\-vsctl\fR commands
+or other database activity.  Thus, a \fBcreate\fR command must
+generally be accompanied by additional commands \fIwithin the same
+\fBovs\-vsctl\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
+It is often unnecessary to specify explicit \fBdestroy\fR commands,
+because (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 means that deleting the
+last reference to a record is sufficient for deleting the record
+itself.  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
@@ -720,10 +742,10 @@ ignored):
 .IP
 .B "\-\- \-\-id=@m create Mirror name=mymirror select-dst-port=@eth0,@eth1 select-src-port=@eth0,@eth1 output-port=@eth2"
 .PP
-Remove the mirror created above from \fBbr0\fR and destroy the Mirror
-record (to avoid having an unreferenced record in the database):
+Remove the mirror created above from \fBbr0\fR, which also destroys
+the Mirror record (since it is now unreferenced):
 .IP
-.B "ovs\-vsctl destroy Mirror mymirror \-\- clear Bridge br0 mirrors"
+.B "remove Bridge br0 mirrors mymirror"
 .SS "Quality of Service (QoS)"
 .PP
 Create a \fBlinux\-htb\fR QoS record that points to a few queues and
@@ -744,7 +766,8 @@ Deconfigure the QoS record above from \fBeth1\fR only:
 .B "ovs\-vsctl clear Port eth1 qos"
 .PP
 To deconfigure the QoS record from both \fBeth0\fR and \fBeth1\fR and
-then delete the QoS record:
+then delete the QoS record (which must be done explicitly because
+unreferenced QoS records are not automatically destroyed):
 .IP
 .B "ovs\-vsctl \-\- destroy QoS eth0 \-\- clear Port eth0 qos \-\- clear Port eth1 qos"
 .PP
@@ -781,10 +804,10 @@ instead use an active timeout of 60 seconds:
 .IP
 .B "ovs\-vsctl set NetFlow br0 active_timeout=60"
 .PP
-Deconfigure the NetFlow settings from \fBbr0\fR and delete the NetFlow
-record (to avoid having an unreferenced record in the database):
+Deconfigure the NetFlow settings from \fBbr0\fR, which also destroys
+the NetFlow record (since it is now unreferenced):
 .IP
-.B "ovs\-vsctl destroy NetFlow br0 \-\- clear Bridge br0 netflow"
+.B "ovs\-vsctl clear Bridge br0 netflow"
 .SS "sFlow"
 .PP
 Configure bridge \fBbr0\fR to send sFlow records to a collector on
@@ -795,10 +818,10 @@ with specific sampling parameters:
 .IP
 .B "\-\- set Bridge br0 sflow=@s"
 .PP
-Deconfigure sFlow from br0 and destroy the sFlow record (to avoid
-having an unreferenced record in the database):
+Deconfigure sFlow from br0, which also destroys the sFlow record
+(since it is now unreferenced):
 .IP
-.B "ovs\-vsctl \-\- destroy sFlow br0 \-\- clear Bridge br0 sflow"
+.B "ovs\-vsctl \-\- clear Bridge br0 sflow"
 .SH "EXIT STATUS"
 .IP "0"
 Successful program execution.
index e5e03f7..80c9048 100644 (file)
@@ -1035,12 +1035,6 @@ cmd_emer_reset(struct vsctl_context *ctx)
     const struct ovsrec_bridge *br;
     const struct ovsrec_port *port;
     const struct ovsrec_interface *iface;
-    const struct ovsrec_mirror *mirror, *next_mirror;
-    const struct ovsrec_controller *ctrl, *next_ctrl;
-    const struct ovsrec_manager *mgr, *next_mgr;
-    const struct ovsrec_netflow *nf, *next_nf;
-    const struct ovsrec_ssl *ssl, *next_ssl;
-    const struct ovsrec_sflow *sflow, *next_sflow;
 
     /* Reset the Open_vSwitch table. */
     ovsrec_open_vswitch_set_manager_options(ctx->ovs, NULL, 0);
@@ -1084,30 +1078,6 @@ cmd_emer_reset(struct vsctl_context *ctx)
         ovsrec_interface_set_ingress_policing_rate(iface, 0);
         ovsrec_interface_set_ingress_policing_burst(iface, 0);
     }
-
-    OVSREC_MIRROR_FOR_EACH_SAFE (mirror, next_mirror, idl) {
-        ovsrec_mirror_delete(mirror);
-    }
-
-    OVSREC_CONTROLLER_FOR_EACH_SAFE (ctrl, next_ctrl, idl) {
-        ovsrec_controller_delete(ctrl);
-    }
-
-    OVSREC_MANAGER_FOR_EACH_SAFE (mgr, next_mgr, idl) {
-        ovsrec_manager_delete(mgr);
-    }
-
-    OVSREC_NETFLOW_FOR_EACH_SAFE (nf, next_nf, idl) {
-        ovsrec_netflow_delete(nf);
-    }
-
-    OVSREC_SSL_FOR_EACH_SAFE (ssl, next_ssl, idl) {
-        ovsrec_ssl_delete(ssl);
-    }
-
-    OVSREC_SFLOW_FOR_EACH_SAFE (sflow, next_sflow, idl) {
-        ovsrec_sflow_delete(sflow);
-    }
 }
 
 static void
@@ -1218,18 +1188,8 @@ cmd_add_br(struct vsctl_context *ctx)
 }
 
 static void
-del_port(struct vsctl_info *info, struct vsctl_port *port)
+del_port(struct vsctl_port *port)
 {
-    struct shash_node *node;
-
-    SHASH_FOR_EACH (node, &info->ifaces) {
-        struct vsctl_iface *iface = node->data;
-        if (iface->port == port) {
-            ovsrec_interface_delete(iface->iface_cfg);
-        }
-    }
-    ovsrec_port_delete(port->port_cfg);
-
     bridge_delete_port((port->bridge->parent
                         ? port->bridge->parent->br_cfg
                         : port->bridge->br_cfg), port->port_cfg);
@@ -1245,18 +1205,18 @@ cmd_del_br(struct vsctl_context *ctx)
     get_info(ctx, &info);
     bridge = find_bridge(&info, ctx->argv[1], must_exist);
     if (bridge) {
-        struct shash_node *node;
-
-        SHASH_FOR_EACH (node, &info.ports) {
-            struct vsctl_port *port = node->data;
-            if (port->bridge == bridge || port->bridge->parent == bridge
-                || !strcmp(port->port_cfg->name, bridge->name)) {
-                del_port(&info, port);
-            }
-        }
         if (bridge->br_cfg) {
-            ovsrec_bridge_delete(bridge->br_cfg);
             ovs_delete_bridge(ctx->ovs, bridge->br_cfg);
+        } else {
+            struct shash_node *node;
+
+            SHASH_FOR_EACH (node, &info.ports) {
+                struct vsctl_port *port = node->data;
+                if (port->bridge == bridge || port->bridge->parent == bridge
+                    || !strcmp(port->port_cfg->name, bridge->name)) {
+                    del_port(port);
+                }
+            }
         }
     }
     free_info(&info);
@@ -1641,7 +1601,7 @@ cmd_del_port(struct vsctl_context *ctx)
             }
         }
 
-        del_port(&info, port);
+        del_port(port);
     }
 
     free_info(&info);
@@ -1773,17 +1733,6 @@ cmd_get_controller(struct vsctl_context *ctx)
     free_info(&info);
 }
 
-static void
-delete_controllers(struct ovsrec_controller **controllers,
-                   size_t n_controllers)
-{
-    size_t i;
-
-    for (i = 0; i < n_controllers; i++) {
-        ovsrec_controller_delete(controllers[i]);
-    }
-}
-
 static void
 cmd_del_controller(struct vsctl_context *ctx)
 {
@@ -1791,13 +1740,9 @@ cmd_del_controller(struct vsctl_context *ctx)
     struct vsctl_bridge *br;
 
     get_info(ctx, &info);
-    br = find_real_bridge(&info, ctx->argv[1], true);
-    verify_controllers(br->br_cfg);
 
-    if (br->ctrl) {
-        delete_controllers(br->ctrl, br->n_ctrl);
-        ovsrec_bridge_set_controller(br->br_cfg, NULL, 0);
-    }
+    br = find_real_bridge(&info, ctx->argv[1], true);
+    ovsrec_bridge_set_controller(br->br_cfg, NULL, 0);
 
     free_info(&info);
 }
@@ -1827,9 +1772,6 @@ cmd_set_controller(struct vsctl_context *ctx)
 
     get_info(ctx, &info);
     br = find_real_bridge(&info, ctx->argv[1], true);
-    verify_controllers(br->br_cfg);
-
-    delete_controllers(br->ctrl, br->n_ctrl);
 
     n = ctx->argc - 2;
     controllers = insert_controllers(ctx->txn, &ctx->argv[2], n);
@@ -1935,28 +1877,12 @@ cmd_get_manager(struct vsctl_context *ctx)
     svec_destroy(&targets);
 }
 
-static void
-delete_managers(const struct vsctl_context *ctx)
-{
-    const struct ovsrec_open_vswitch *ovs = ctx->ovs;
-    size_t i;
-
-    /* Delete Manager rows pointed to by 'manager_options' column. */
-    for (i = 0; i < ovs->n_manager_options; i++) {
-        ovsrec_manager_delete(ovs->manager_options[i]);
-    }
-
-    /* Delete 'Manager' row refs in 'manager_options' column. */
-    ovsrec_open_vswitch_set_manager_options(ovs, NULL, 0);
-}
-
 static void
 cmd_del_manager(struct vsctl_context *ctx)
 {
     const struct ovsrec_open_vswitch *ovs = ctx->ovs;
 
-    verify_managers(ovs);
-    delete_managers(ctx);
+    ovsrec_open_vswitch_set_manager_options(ovs, NULL, 0);
 }
 
 static void
@@ -1982,8 +1908,6 @@ cmd_set_manager(struct vsctl_context *ctx)
 {
     const size_t n = ctx->argc - 1;
 
-    verify_managers(ctx->ovs);
-    delete_managers(ctx);
     insert_managers(ctx, &ctx->argv[1], n);
 }
 
@@ -2027,13 +1951,7 @@ pre_cmd_del_ssl(struct vsctl_context *ctx)
 static void
 cmd_del_ssl(struct vsctl_context *ctx)
 {
-    struct ovsrec_ssl *ssl = ctx->ovs->ssl;
-
-    if (ssl) {
-        ovsrec_open_vswitch_verify_ssl(ctx->ovs);
-        ovsrec_ssl_delete(ssl);
-        ovsrec_open_vswitch_set_ssl(ctx->ovs, NULL);
-    }
+    ovsrec_open_vswitch_set_ssl(ctx->ovs, NULL);
 }
 
 static void
@@ -2046,12 +1964,8 @@ static void
 cmd_set_ssl(struct vsctl_context *ctx)
 {
     bool bootstrap = shash_find(&ctx->options, "--bootstrap");
-    struct ovsrec_ssl *ssl = ctx->ovs->ssl;
+    struct ovsrec_ssl *ssl;
 
-    ovsrec_open_vswitch_verify_ssl(ctx->ovs);
-    if (ssl) {
-        ovsrec_ssl_delete(ssl);
-    }
     ssl = ovsrec_ssl_insert(ctx->txn);
 
     ovsrec_ssl_set_private_key(ssl, ctx->argv[1]);
@@ -2351,7 +2265,7 @@ get_column(const struct vsctl_table_class *table, const char *column_name,
     }
 }
 
-static struct uuid *
+static struct ovsdb_symbol *
 create_symbol(struct ovsdb_symbol_table *symtab, const char *id, bool *newp)
 {
     struct ovsdb_symbol *symbol;
@@ -2370,7 +2284,7 @@ create_symbol(struct ovsdb_symbol_table *symtab, const char *id, bool *newp)
                     id);
     }
     symbol->created = true;
-    return &symbol->uuid;
+    return symbol;
 }
 
 static void
@@ -2578,13 +2492,19 @@ cmd_get(struct vsctl_context *ctx)
     table = get_table(table_name);
     row = must_get_row(ctx, table, record_id);
     if (id) {
+        struct ovsdb_symbol *symbol;
         bool new;
 
-        *create_symbol(ctx->symtab, id, &new) = row->uuid;
+        symbol = create_symbol(ctx->symtab, id, &new);
         if (!new) {
             vsctl_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;
@@ -3093,18 +3013,42 @@ cmd_clear(struct vsctl_context *ctx)
 }
 
 static void
-cmd_create(struct vsctl_context *ctx)
+pre_create(struct vsctl_context *ctx)
 {
     const char *id = shash_find_data(&ctx->options, "--id");
     const char *table_name = ctx->argv[1];
     const struct vsctl_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 vsctl_context *ctx)
+{
+    const char *id = shash_find_data(&ctx->options, "--id");
+    const char *table_name = ctx->argv[1];
+    const struct vsctl_table_class *table = get_table(table_name);
     const struct ovsdb_idl_row *row;
     const struct uuid *uuid;
     int i;
 
-    uuid = id ? create_symbol(ctx->symtab, id, NULL) : NULL;
+    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;
+    }
 
-    table = get_table(table_name);
     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);
@@ -3402,6 +3346,17 @@ do_vsctl(const char *args, struct vsctl_command *commands, size_t n_commands,
                         "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);
@@ -3577,7 +3532,7 @@ static const struct vsctl_command_syntax all_commands[] = {
     {"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, NULL, cmd_create, post_create, "--id=", 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",
      RW},
     {"wait-until", 2, INT_MAX, pre_cmd_wait_until, cmd_wait_until, NULL, "",
index 6074462..4a80289 100644 (file)
@@ -497,14 +497,6 @@ del_port(const struct ovsrec_bridge *br, const struct ovsrec_port *port)
     }
     ovsrec_bridge_set_ports(br, ports, n);
     free(ports);
-
-    /* Delete all of the port's interfaces. */
-    for (i = 0; i < port->n_interfaces; i++) {
-        ovsrec_interface_delete(port->interfaces[i]);
-    }
-
-    /* Delete the port itself. */
-    ovsrec_port_delete(port);
 }
 
 /* Delete 'iface' from 'port' (which must be within 'br').  If 'iface' was
@@ -530,7 +522,6 @@ del_interface(const struct ovsrec_bridge *br,
         }
         ovsrec_port_set_interfaces(port, ifaces, n);
         free(ifaces);
-        ovsrec_interface_delete(iface);
     }
 }
 
@@ -591,24 +582,6 @@ del_bridge(struct ovsdb_idl *idl,
 
     ovsdb_idl_txn_add_comment(txn, "ovs-brcompatd: delbr %s", br_name);
 
-    /* Delete everything that the bridge points to, then delete the bridge
-     * itself. */
-    while (br->n_ports > 0) {
-        del_port(br, br->ports[0]);
-    }
-    for (i = 0; i < br->n_mirrors; i++) {
-        ovsrec_mirror_delete(br->mirrors[i]);
-    }
-    if (br->netflow) {
-        ovsrec_netflow_delete(br->netflow);
-    }
-    if (br->sflow) {
-        ovsrec_sflow_delete(br->sflow);
-    }
-    for (i = 0; i < br->n_controller; i++) {
-        ovsrec_controller_delete(br->controller[i]);
-    }
-
     /* Remove 'br' from the vswitch's list of bridges. */
     bridges = xmalloc(sizeof *ovs->bridges * ovs->n_bridges);
     for (i = n = 0; i < ovs->n_bridges; i++) {
@@ -619,9 +592,6 @@ del_bridge(struct ovsdb_idl *idl,
     ovsrec_open_vswitch_set_bridges(ovs, bridges, n);
     free(bridges);
 
-    /* Delete the bridge itself. */
-    ovsrec_bridge_delete(br);
-
     return commit_txn(txn, true);
 }
 
index 1ab56e6..5988698 100644 (file)
@@ -3,92 +3,36 @@ digraph Open_vSwitch {
        margin="0";
        node [shape=box];
        edge [dir=none, arrowhead=none, arrowtail=none];
-       Bridge;
+       Bridge [style=bold];
        Bridge -> sFlow [label="sflow"];
        Bridge -> Mirror [label="mirrors"];
        Bridge -> Port [label="ports"];
        Bridge -> Controller [label="controller"];
        Bridge -> NetFlow [label="netflow"];
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       QoS;
+       QoS [style=bold];
        QoS -> Queue [label="queues value"];
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       Monitor;
+       Monitor [style=bold];
        Monitor -> Maintenance_Point [label="remote_mps"];
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       sFlow;
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       Open_vSwitch;
+       sFlow [style=bold];
+       Open_vSwitch [style=bold];
        Open_vSwitch -> Bridge [label="bridges"];
        Open_vSwitch -> Capability [label="capabilities value"];
        Open_vSwitch -> SSL [label="ssl"];
        Open_vSwitch -> Manager [label="manager_options"];
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       Controller;
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       Queue;
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       SSL;
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       Manager;
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       Capability;
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       Mirror;
-       Mirror -> Port [constraint=false, label="select_src_port"];
-       Mirror -> Port [constraint=false, label="output_port"];
-       Mirror -> Port [constraint=false, label="select_dst_port"];
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       Interface;
+       Controller [style=bold];
+       Queue [style=bold];
+       SSL [style=bold];
+       Manager [style=bold];
+       Capability [style=bold];
+       Mirror [style=bold];
+       Mirror -> Port [style=dotted, constraint=false, label="select_src_port"];
+       Mirror -> Port [style=dotted, constraint=false, label="output_port"];
+       Mirror -> Port [style=dotted, constraint=false, label="select_dst_port"];
+       Interface [style=bold];
        Interface -> Monitor [label="monitor"];
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       NetFlow;
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       Maintenance_Point;
-       size="6.5,4";
-       margin="0";
-       node [shape=box];
-       edge [dir=none, arrowhead=none, arrowtail=none];
-       Port;
+       NetFlow [style=bold];
+       Maintenance_Point [style=bold];
+       Port [style=bold];
        Port -> QoS [label="qos"];
        Port -> Interface [label="interfaces"];
 }
index 3537d28..7618e2d 100644 (file)
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
- "version": "2.0.0",
- "cksum": "4107852581 15651",
+ "version": "2.1.0",
+ "cksum": "1990266641 15714",
  "tables": {
    "Open_vSwitch": {
      "columns": {
@@ -43,6 +43,7 @@
        "system_version": {
          "type": {"key": {"type": "string"},
                   "min": 0, "max": 1}}},
+     "isRoot": true,
      "maxRows": 1},
    "Capability": {
      "columns": {
                   "min": 0, "max": "unlimited"}},
        "external_ids": {
          "type": {"key": "string", "value": "string",
-                  "min": 0, "max": "unlimited"}}}},
+                  "min": 0, "max": "unlimited"}}},
+     "isRoot": true},
    "Queue": {
      "columns": {
        "other_config": {
                   "min": 0, "max": "unlimited"}},
        "external_ids": {
          "type": {"key": "string", "value": "string",
-                  "min": 0, "max": "unlimited"}}}},
+                  "min": 0, "max": "unlimited"}}},
+     "isRoot": true},
    "Mirror": {
      "columns": {
        "name": {
index 99bcaaa..86296e8 100644 (file)
-.\" Generated from vswitch.gv with cksum "3734436941 2390"
+.\" Generated from vswitch.gv with cksum "1122829786 1269"
 .PS
 linethick = 1;
+linethick = 0.5;
 box at 2.320997253,3.1110975 wid 0.5020540998 height 0.296295 "Bridge"
+box at 2.320997253,3.1110975 wid 0.446498544244444 height 0.240739444444444
+linethick = 0.5;
 box at 0.2304523251,2.37036 wid 0.4609046502 height 0.296295 "sFlow"
+box at 0.2304523251,2.37036 wid 0.405349094644444 height 0.240739444444444
+linethick = 0.5;
 box at 0.847759254,2.37036 wid 0.4855919496 height 0.296295 "Mirror"
+box at 0.847759254,2.37036 wid 0.430036394044444 height 0.240739444444444
+linethick = 0.5;
 box at 2.320997253,2.37036 wid 0.4444425 height 0.296295 "Port"
+box at 2.320997253,2.37036 wid 0.388886944444444 height 0.240739444444444
+linethick = 0.5;
 box at 3.045260751,2.37036 wid 0.707789496 height 0.296295 "Controller"
+box at 3.045260751,2.37036 wid 0.652233940444444 height 0.240739444444444
+linethick = 0.5;
 box at 3.851835,2.37036 wid 0.609064002 height 0.296295 "NetFlow"
+box at 3.851835,2.37036 wid 0.553508446444444 height 0.240739444444444
+linethick = 0.5;
 box at 2.057590998,1.6296225 wid 0.4444425 height 0.296295 "QoS"
+box at 2.057590998,1.6296225 wid 0.388886944444444 height 0.240739444444444
+linethick = 0.5;
 box at 1.991754249,0.888885 wid 0.5102851749 height 0.296295 "Queue"
+box at 1.991754249,0.888885 wid 0.454729619344444 height 0.240739444444444
+linethick = 0.5;
 box at 2.938238997,0.888885 wid 0.5761278498 height 0.296295 "Monitor"
+box at 2.938238997,0.888885 wid 0.520572294244444 height 0.240739444444444
+linethick = 0.5;
 box at 2.938238997,0.1481475 wid 1.218128004 height 0.296295 "Maintenance_Point"
+box at 2.938238997,0.1481475 wid 1.16257244844444 height 0.240739444444444
+linethick = 0.5;
 box at 3.637850751,3.851835 wid 0.954721749 height 0.296295 "Open_vSwitch"
+box at 3.637850751,3.851835 wid 0.899166193444444 height 0.240739444444444
+linethick = 0.5;
 box at 3.061734753,3.1110975 wid 0.699611754 height 0.296295 "Capability"
+box at 3.061734753,3.1110975 wid 0.644056198444444 height 0.240739444444444
+linethick = 0.5;
 box at 4.22220375,3.1110975 wid 0.4444425 height 0.296295 "SSL"
+box at 4.22220375,3.1110975 wid 0.388886944444444 height 0.240739444444444
+linethick = 0.5;
 box at 4.905341502,3.1110975 wid 0.633715746 height 0.296295 "Manager"
+box at 4.905341502,3.1110975 wid 0.578160190444444 height 0.240739444444444
+linethick = 0.5;
 box at 2.872402248,1.6296225 wid 0.641952747 height 0.296295 "Interface"
+box at 2.872402248,1.6296225 wid 0.586397191444444 height 0.240739444444444
+linethick = 1;
 spline -> from 2.072227971,3.066534732 to 2.072227971,3.066534732 to 1.825829049,3.018534942 to 1.439934441,2.932787169 to 1.119343251,2.8148025 to 0.887818338,2.729588058 to 0.637567581,2.60087751 to 0.4617876093,2.503870527
 "sflow" at 1.271579622,2.74072875
+linethick = 1;
 spline -> from 2.071042791,2.98546842 to 2.071042791,2.98546842 to 1.796021772,2.847157914 to 1.357208877,2.626536657 to 1.086632283,2.490477993
 "mirrors" at 1.9259175,2.74072875
+linethick = 1;
 spline -> from 2.320997253,2.96117223 to 2.320997253,2.96117223 to 2.320997253,2.832698718 to 2.320997253,2.648462487 to 2.320997253,2.520048234
 "ports" at 2.469144753,2.74072875
+linethick = 1;
 spline -> from 2.495218713,2.960816676 to 2.495218713,2.960816676 to 2.546240712,2.915068728 to 2.601410841,2.863868952 to 2.650180998,2.8148025 to 2.743336146,2.721054762 to 2.842120899,2.609944137 to 2.917557606,2.522300076
 "controller" at 3.065823624,2.74072875
+linethick = 1;
 spline -> from 2.571070233,2.988964701 to 2.571070233,2.988964701 to 2.594773833,2.979424002 to 2.61865521,2.970594411 to 2.641943997,2.96295 to 2.957675949,2.859720822 to 3.079038381,2.966624058 to 3.374444496,2.8148025 to 3.514473513,2.742862074 to 3.643184061,2.618003361 to 3.731598489,2.518922313
 "netflow" at 3.802472253,2.74072875
+linethick = 0.5;
 spline -> from 1.091017449,2.37036 to 1.091017449,2.37036 to 1.370542152,2.37036 to 1.825414236,2.37036 to 2.096701938,2.37036
 "select_src_port" at 1.588496754,2.44443375
+linethick = 0.5;
 spline -> from 1.067787921,2.221501392 to 1.067787921,2.221501392 to 1.095284097,2.208345894 to 1.123787676,2.197086684 to 1.152231996,2.189264496 to 1.526156286,2.08650939 to 1.651429812,2.084257548 to 2.024702253,2.189264496 to 2.052553983,2.197145943 to 2.080346454,2.208405153 to 2.107190781,2.221501392
 "output_port" at 1.588496754,2.263338246
+linethick = 0.5;
 spline -> from 0.905240484,2.221264356 to 0.905240484,2.221264356 to 0.953892123,2.117798142 to 1.034780658,1.987843155 to 1.152231996,1.9259175 to 1.495282347,1.745118291 to 1.682837082,1.742984967 to 2.024702253,1.9259175 to 2.140790634,1.988080191 to 2.21924955,2.117975919 to 2.266123419,2.221442133
 "select_dst_port" at 1.588496754,1.99999125
+linethick = 1;
 spline -> from 2.267664153,2.22043473 to 2.267664153,2.22043473 to 2.221975464,2.091961218 to 2.156494269,1.907724987 to 2.11080558,1.779310734
 "qos" at 2.312760252,1.99999125
+linethick = 1;
 spline -> from 2.43258195,2.22043473 to 2.43258195,2.22043473 to 2.528225976,2.091961218 to 2.665351302,1.907724987 to 2.760995328,1.779310734
 "interfaces" at 2.930001996,1.99999125
+linethick = 1;
 spline -> from 2.023220778,1.479637971 to 2.023220778,1.479637971 to 2.013917115,1.433001138 to 2.005028265,1.381268031 to 1.99999125,1.3333275 to 1.989620925,1.235076078 to 1.98754686,1.123965453 to 1.988080191,1.037447313
 "queues value" at 2.378597001,1.25925375
+linethick = 1;
 spline -> from 2.938238997,0.73895973 to 2.938238997,0.73895973 to 2.938238997,0.610486218 to 2.938238997,0.4262736906 to 2.938238997,0.2978238822
 "remote_mps" at 3.288044874,0.51851625
+linethick = 1;
 spline -> from 3.159808398,3.796309317 to 3.159808398,3.796309317 to 2.96591295,3.754828017 to 2.749321305,3.682295001 to 2.584344249,3.55554 to 2.486211345,3.480162552 to 2.417352387,3.356903832 to 2.374685907,3.259245
 "bridges" at 2.790091497,3.48146625
+linethick = 1;
 spline -> from 3.219067398,3.702146766 to 3.219067398,3.702146766 to 3.159926916,3.663332121 to 3.106830852,3.615095295 to 3.069971754,3.55554 to 3.015927546,3.468251493 to 3.015809028,3.35168904 to 3.028194159,3.25983759
 "capabilities value" at 3.55554,3.48146625
+linethick = 1;
 spline -> from 3.884901522,3.703568982 to 3.884901522,3.703568982 to 3.941612385,3.661080279 to 3.997849176,3.611065683 to 4.041108246,3.55554 to 4.109256096,3.468132975 to 4.155478116,3.351570522 to 4.184159472,3.259778331
 "ssl" at 4.205729748,3.48146625
+linethick = 1;
 spline -> from 4.020249078,3.702146766 to 4.020249078,3.702146766 to 4.117848651,3.659065473 to 4.22101857,3.609110136 to 4.312751502,3.55554 to 4.46042493,3.469258896 to 4.614616848,3.352696443 to 4.728690423,3.260548698
 "manager_options" at 5.024689128,3.48146625
+linethick = 1;
 spline -> from 2.885735523,1.47969723 to 2.885735523,1.47969723 to 2.89717251,1.351223718 to 2.913527994,1.166987487 to 2.924964981,1.038573234
 "monitor" at 3.139897374,1.25925375
 .PE
index e0245cd..89354da 100644 (file)
@@ -1,15 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
 <database title="Open vSwitch Configuration Database">
-  <p>A database with this schema holds the configuration for one Open
-    vSwitch daemon.  The root of the configuration for the daemon is
-    the <ref table="Open_vSwitch"/> table, which must have exactly one
+  <p>
+    A database with this schema holds the configuration for one Open
+    vSwitch daemon.  The top-level configuration for the daemon is the
+    <ref table="Open_vSwitch"/> table, which must have exactly one
     record.  Records in other tables are significant only when they
-    can be reached directly or indirectly from the
-    <ref table="Open_vSwitch"/> table.</p>
+    can be reached directly or indirectly from the <ref
+    table="Open_vSwitch"/> table.  Records that are not reachable from
+    the <ref table="Open_vSwitch"/> table are automatically deleted
+    from the database, except for records in a few distinguished
+    ``root set'' tables noted below.
+  </p>
 
   <table name="Open_vSwitch" title="Open vSwitch configuration.">
-    Configuration for an Open vSwitch daemon.  There must be exactly one record
-    in the <ref table="Open_vSwitch"/> table.
+    Configuration for an Open vSwitch daemon.  There must be exactly
+    one record in the <ref table="Open_vSwitch"/> table.
 
     <group title="Configuration">
       <column name="bridges">
index 13b9d40..7300981 100755 (executable)
@@ -341,7 +341,18 @@ function start {
         cksum=`$ovsdb_tool db-cksum "$OVSDB_SERVER_DB" | awk '{print $1}'`
         cp "$OVSDB_SERVER_DB" "$OVSDB_SERVER_DB.backup$version-$cksum"
 
-        # Upgrade or downgrade schema and compact database.
+        # Compact database.  This is important if the old schema did not enable
+        # garbage collection (i.e. if it did not have any tables with "isRoot":
+        # true) but the new schema does.  In that situation the old database
+        # may contain a transaction that creates a record followed by a
+        # transaction that creates the first use of the record.  Replaying that
+        # series of transactions against the new database schema (as "convert"
+        # does) would cause the record to be dropped by the first transaction,
+        # then the second transaction would cause a referential integrity
+        # failure (for a strong reference).
+        $ovsdb_tool -vANY:console:emer compact "$OVSDB_SERVER_DB"
+
+        # Upgrade or downgrade schema.
         $ovsdb_tool -vANY:console:emer convert "$OVSDB_SERVER_DB" "$VSWITCHD_OVSDB_SCHEMA"
     fi