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"
             
             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
             # 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;
 }
 
     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,
 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;
 {
     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) {
 
         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);
 
             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>");
             }
             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);
             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
 }
 
 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)
 {
                        const struct json *json,
                        struct ovsdb_symbol_table *symtab)
 {
+    enum ovsdb_atomic_type type = base->type;
+
     switch (type) {
     case OVSDB_TYPE_VOID:
         NOT_REACHED();
     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:
         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:
 
     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
  *
  * 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,
 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;
 
 {
     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;
     }
     if (error) {
         return error;
     }
@@ -440,9 +467,12 @@ ovsdb_atom_to_json(const union ovsdb_atom *atom, enum ovsdb_atomic_type type)
 }
 
 static char *
 }
 
 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();
     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 == '@') {
 
     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);
         }
         } 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
  *        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
  *
  * 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;
 
     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;
     }
     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 = 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;
 }
     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? */
 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);
 };
 
 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.
  *
  * 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;
 
 struct ovsdb_idl_table_class {
     char *name;
+    bool is_root;
     const struct ovsdb_idl_column *columns;
     size_t n_columns;
     size_t allocation_size;
     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
 
         "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.
 
     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.
 
         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
     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>
 
 
 <column-schema>
 
index caca9f8..52de5e8 100755 (executable)
@@ -1,6 +1,6 @@
 #! /usr/bin/perl
 
 #! /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.
 #
 # 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;
         $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";
         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;
     } elsif (/edge/) {
         my (undef, $tail, $head, $n, $rest) = split(' ', $_, 5);
         my @xy;
@@ -51,6 +58,8 @@ while (<>) {
         }
         my ($style, $color) = split(' ', $rest);
 
         }
         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]";
         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
 .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
 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")
 .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['label'] = '"%s"' % label
         if baseType.ref_type == 'weak':
             options['constraint'] = 'false'
+            options['style'] = 'dotted'
         print "\t%s -> %s [%s];" % (
             tableName,
             baseType.ref_table,
         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
     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():
     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)
         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 "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
         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';
 }
 
     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)
 {
 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;
 }
     *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;
 {
     struct json *json, *tables;
     struct shash_node *node;
+    bool default_is_root;
 
     json = json_object_create();
     json_object_put_string(json, "name", schema->name);
 
     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);
     }
 
         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,
     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);
 
     }
     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.
  *
  * 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,
 
 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;
 {
     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->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);
 
     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;
 
     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;
 
     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;
                              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;
     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);
                                   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;
     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,
 
     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;
 
     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;
 }
 
     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 *
 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;
 {
     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 (!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();
 
 
     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.
  *
  * 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 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 *);
 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;
                                                  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);
 
 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. */
 };
 
     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,
 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;
 }
 
     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,
 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;
     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;
         }
         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) {
         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;
     }
         }
         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)
 {
 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;
     }
 
         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;
     }
 
     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;
     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.
 #
 # 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")
 
                 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")
     @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):
         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 = {}
         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
         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):
                          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.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):
 
     @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])
         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:
         parser.finish()
 
         if max_rows == None:
@@ -128,12 +153,25 @@ class TableSchema(object):
             columns[columnName] = ColumnSchema.from_json(columnJson,
                                                          columnName)
 
             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
         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():
 
         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_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])], 
   [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>
 cp stdout out2
 AT_CHECK([perl $srcdir/uuidfilt.pl out1 out2], [0], 
   [[<0>
+
 _uuid               : <0>
 controller          : []
 datapath_id         : []
 _uuid               : <0>
 controller          : []
 datapath_id         : []
@@ -572,8 +575,10 @@ AT_CHECK(
 name                : "br0"
 datapath_type       : ""
 ]], [ignore], [test ! -e pid || kill `cat pid`])
 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],
   [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],
 ], [], [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])
   [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_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])
   [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>
   [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
 _uuid               : <0>
 active_timeout      : 0
 add_id_to_interface : false
@@ -806,13 +815,44 @@ name                : "br0"
 OVS_VSCTL_CLEANUP
 AT_CLEANUP
 
 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
 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>
   [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"}}}}}}]])
 
                                     "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
 # 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",[]]}]}]
 [{"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
 ]])])
 
 EXECUTION_EXAMPLES
index 70f8ac2..cf206c4 100644 (file)
@@ -1,6 +1,6 @@
 AT_BANNER([OVSDB -- tables])
 
 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"}}}]])
 
   [[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}]])
 
       "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}']],
 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 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"
            "    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;
 do_parse_table(int argc OVS_UNUSED, char *argv[])
 {
     struct ovsdb_table_schema *ts;
+    bool default_is_root;
     struct json *json;
 
     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);
     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);
 }
 
     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-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 },
     { "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.
 #
 # 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)
 
     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)
     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))
 
 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 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
   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-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,))}
 
                 "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"
 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"
 .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.
 .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.
 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 "\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
 .
 .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
 .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
 .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
 .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
 .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
 .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
 .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
 .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
 .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
 .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
 .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.
 .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_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);
 
     /* 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_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
 }
 
 static void
@@ -1218,18 +1188,8 @@ cmd_add_br(struct vsctl_context *ctx)
 }
 
 static void
 }
 
 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);
     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) {
     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) {
         if (bridge->br_cfg) {
-            ovsrec_bridge_delete(bridge->br_cfg);
             ovs_delete_bridge(ctx->ovs, 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);
         }
     }
     free_info(&info);
@@ -1641,7 +1601,7 @@ cmd_del_port(struct vsctl_context *ctx)
             }
         }
 
             }
         }
 
-        del_port(&info, port);
+        del_port(port);
     }
 
     free_info(&info);
     }
 
     free_info(&info);
@@ -1773,17 +1733,6 @@ cmd_get_controller(struct vsctl_context *ctx)
     free_info(&info);
 }
 
     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)
 {
 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);
     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);
 }
 
     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);
 
     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);
 
     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);
 }
 
     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;
 
 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
 }
 
 static void
@@ -1982,8 +1908,6 @@ cmd_set_manager(struct vsctl_context *ctx)
 {
     const size_t n = ctx->argc - 1;
 
 {
     const size_t n = ctx->argc - 1;
 
-    verify_managers(ctx->ovs);
-    delete_managers(ctx);
     insert_managers(ctx, &ctx->argv[1], n);
 }
 
     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)
 {
 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
 }
 
 static void
@@ -2046,12 +1964,8 @@ static void
 cmd_set_ssl(struct vsctl_context *ctx)
 {
     bool bootstrap = shash_find(&ctx->options, "--bootstrap");
 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]);
     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;
 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;
                     id);
     }
     symbol->created = true;
-    return &symbol->uuid;
+    return symbol;
 }
 
 static void
 }
 
 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) {
     table = get_table(table_name);
     row = must_get_row(ctx, table, record_id);
     if (id) {
+        struct ovsdb_symbol *symbol;
         bool new;
 
         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);
         }
         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;
     }
     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
 }
 
 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;
 {
     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;
 
     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);
     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);
         }
                         "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);
     }
 
     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},
     {"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, "",
     {"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);
     }
     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
 }
 
 /* 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_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);
 
 
     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++) {
     /* 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);
 
     ovsrec_open_vswitch_set_bridges(ovs, bridges, n);
     free(bridges);
 
-    /* Delete the bridge itself. */
-    ovsrec_bridge_delete(br);
-
     return commit_txn(txn, true);
 }
 
     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];
        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"];
        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"];
        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"];
        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"];
        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"];
        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"];
 }
        Port -> QoS [label="qos"];
        Port -> Interface [label="interfaces"];
 }
index 3537d28..7618e2d 100644 (file)
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
 {"name": "Open_vSwitch",
- "version": "2.0.0",
- "cksum": "4107852581 15651",
+ "version": "2.1.0",
+ "cksum": "1990266641 15714",
  "tables": {
    "Open_vSwitch": {
      "columns": {
  "tables": {
    "Open_vSwitch": {
      "columns": {
@@ -43,6 +43,7 @@
        "system_version": {
          "type": {"key": {"type": "string"},
                   "min": 0, "max": 1}}},
        "system_version": {
          "type": {"key": {"type": "string"},
                   "min": 0, "max": 1}}},
+     "isRoot": true,
      "maxRows": 1},
    "Capability": {
      "columns": {
      "maxRows": 1},
    "Capability": {
      "columns": {
                   "min": 0, "max": "unlimited"}},
        "external_ids": {
          "type": {"key": "string", "value": "string",
                   "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": {
    "Queue": {
      "columns": {
        "other_config": {
                   "min": 0, "max": "unlimited"}},
        "external_ids": {
          "type": {"key": "string", "value": "string",
                   "min": 0, "max": "unlimited"}},
        "external_ids": {
          "type": {"key": "string", "value": "string",
-                  "min": 0, "max": "unlimited"}}}},
+                  "min": 0, "max": "unlimited"}}},
+     "isRoot": true},
    "Mirror": {
      "columns": {
        "name": {
    "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;
 .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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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">
 <?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
     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.">
 
   <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">
 
     <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"
 
         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
 
         $ovsdb_tool -vANY:console:emer convert "$OVSDB_SERVER_DB" "$VSWITCHD_OVSDB_SCHEMA"
     fi