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